E
E
Evgeny Ivanov2019-04-03 08:02:07
Delphi
Evgeny Ivanov, 2019-04-03 08:02:07

How to work with streams correctly?

Often in my programs there are different errors. Found out that maybe I'm not working correctly with threads. In programs, I usually need to perform some actions that take 2-3 minutes. For example - reading a large file or database.
Therefore, I create 1 thread and perform all actions in it.
(The program usually consists of 1 module (1 form).)
Interested in your opinion, namely - what am I doing wrong and how to write this test case correctly.
How to work with streams correctly? Below is an example code with comments.
How do I see my code. While the thread is running (2-3 minutes), I need to show messages to the user. At what stage the program is running. To do this, I use the Synchronize method and my Show_message procedure.
In shared variables (above implementation) I arrange variables, arrays, etc. They should be visible from anywhere in the program. Including from the stream.
Then I use these objects in any part of my program.
But since I cannot pass the parameter (message text) there, I use the output_message variable for this, to which I "assign the message text". And in procedure Show_message "I take away the text from this general variable" and I transfer to a label on the form.
Sometimes I create objects outside of the thread and use them in the thread (TStringList.Create).
Often I work with shared variables/objects in all parts of the program (my_array).
The code

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    start_thread_button: TButton;
    information_label: TLabel;
    procedure start_thread_buttonClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
//Класс потока TMyThread
TMyThread = class(TThread)
protected
procedure Execute; override;
procedure Show_message;
end;
//Конец класса потока

var
  Form1: TForm1;

//Общие переменные
MyThread: TMyThread; //Поток
output_message:string; //Переменная для передачи сообщения "на форму"
file_list:TStringlist; //Список содержащий файлы
my_array: array of array [1..2] of string;  //Массив для примера

implementation

{$R *.dfm}



//Вывод сообщения
procedure TMyThread.Show_message;
begin
//Передача текста из общей переменной (своего рода буффер) на форму (в VLC компонент )
form1.information_label.Caption:=output_message;
end;

//Запуск
procedure TForm1.start_thread_buttonClick(Sender: TObject);
begin
//Создали объект вне потока TMyThread
file_list:=TStringList.Create;
//Работаем с объектом из общих переменных
SetLength(my_array, 1);
//Запускаем поток
MyThread:=TMyThread.Create(False);
end;


//Процедура потока TMyThread
procedure TMyThread.Execute;
begin
//Работаем с объектами, созданными вне потока TMyThread
file_list.Add('добавим элемент листа');
file_list.Destroy;
//Работаем с объектом из общих переменных в потоке
my_array[1,1]:='элемент массива';
//Вывод сообщения (работа с VLC должно быть синхронизировано)
output_message:='Вывод тестового сообщения в information_label на форме'; Synchronize(Show_message);
end;

end.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
S
Stockholm Syndrome, 2019-04-03
@StockholmSyndrome

it is not necessary to create a global variable output_message
in Syncronize, you can pass an anonymous procedure, then the message can be stored in a local variable of the thread procedure

procedure TMyThread.Execute;
var 
  Msg: string;
begin 
  ... 
  Syncronize(procedure() begin 
    Show_message(Msg); 
  end);
end.

PS so it's better not to release objects, it's
better to use Free or FreeAndNil
file_list.Free; 
// или
FreeAndNil(file_list);

K
Konstantin Tsvetkov, 2019-04-03
@tsklab

While participating in the answer to the question How to fix the listerror list index out of b... wanted to suggest using a stream for a single file. Each file will be processed by a separate thread.

The form:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ListBox1: TListBox;
    procedure Button1Click(Sender: TObject);
  private { Private declarations }
  public  { Public declarations }
  end;

var Form1: TForm1;

implementation

{$R *.dfm}

uses Unit2;

procedure TForm1.Button1Click(Sender: TObject);
  var fl: String;
      tr: TSome;
begin
  fl := ExtractFilePath( Application.ExeName ) + GUIDToString( TGUID.NewGuid ) + '.thr';
  tr := TSome.Create( fl );
  ListBox1.Items.Add( fl );
end;
end.
Flow:
unit Unit2;

interface

uses  System.Classes, System.SysUtils;

type
  TSome = class(TThread)
  private   { Private declarations }
    FFile: String;
    FMess: String;
    procedure UpdateForm;
  protected
    procedure Execute; override;
  public    { Public declarations }
    constructor Create( sFile: String );
  end;

implementation

uses Unit1;

constructor TSome.Create( sFile: String );
begin
  FFile := sFile;
  inherited Create( False );
end;

procedure TSome.Execute;
  var outfile: TextFile;
      tr: String;
begin
  while not Self.Terminated do begin
    tr := IntToStr( Self.Handle ) + ' - '+ DateTimeToStr( Now ());
    AssignFile( outfile, FFile );
    Rewrite( outfile );
    Writeln( outfile, tr );
    CloseFile( outfile );
    Self.FMess := tr;
    Synchronize( UpdateForm );
    Sleep( 1000 );
  end;
end;

procedure TSome.UpdateForm;
begin
  Form1.ListBox1.Items.Add( Self.FMess );
end;

end.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question