Задать вопрос

Как правильно работать с потоками?

Часто в моих программах возникают разные ошибки. Выяснил, что возможно я не правильно работаю с потоками. В программах, обычно, мне нужно выполнить некоторые действия, занимающие 2-3 минуты. Например - чтение большого файла или базы.
Поэтому я создаю 1 поток и произвожу все действия в нем.
(Программа обычно состоит из 1 модуля (1 формы).)

Интересует ваше мнение, а именно - что я делаю не так и как правильно написать данный тестовый пример.
Как правильно работать с потоками? Ниже пример кода с комментариями.

Как я вижу свой код.
В общих переменных (выше implementation) я располагаю переменные, массивы и т.д. Они должны быть видны из любого места программы. В том числе из потока.
Далее я использую эти объекты в любой части моей программы.

Во время работы потока (2-3 минуты) мне надо показывать пользователю сообщения. На каком этапе выполнение программы. Для этого я использую метод Synchronize и свою процедуру Show_message.
Но так как я не могу передать туда параметр (текст сообщения) то использую для этого переменную output_message, которой "присваиваю текст сообщения". А в процедуре Show_message "забираю из этой общей переменной текст" и передаю в ярлык на форме.

Иногда я создаю объекты вне потока и использую их в потоке (TStringList.Create).
Часто я работаю с общими переменными/объектами во всех частях программы (my_array).

Код
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.
  • Вопрос задан
  • 187 просмотров
Подписаться 1 Простой Комментировать
Пригласить эксперта
Ответы на вопрос 2
@StockholmSyndrome
необязательно создавать глобальную переменную output_message
в Syncronize можно передать анонимную процедуру, тогда сообщение можно хранить в локальной переменной процедуры потока
procedure TMyThread.Execute;
var 
  Msg: string;
begin 
  ... 
  Syncronize(procedure() begin 
    Show_message(Msg); 
  end);
end.

P.S. так объекты лучше не освобождать
file_list.Destroy;
лучше использовать Free или FreeAndNil
file_list.Free; 
// или
FreeAndNil(file_list);
Ответ написан
Комментировать
tsklab
@tsklab
Здесь отвечаю на вопросы.
Участвуя в ответе на вопрос Как исправить ошибку listerror list index out of b... хотел предложить использовать поток для отдельного файла. Каждый файл будет обрабатываться отдельным потоком.
Форма:
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.
Поток:
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.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы