Пример простого потока Delphi

#multithreading #delphi

Вопрос:

Я новичок в потоковой обработке в Delphi. итак, я пытаюсь создать простое приложение для запроса, которое вызывает небольшой вызов базы данных и занимает немного времени, поэтому я хочу предупредить пользователя о том, что существует фоновый процесс, и нужно быть терпеливым.

Я перепробовал много примеров, но ни один из них не работает для меня, пожалуйста, может кто-нибудь показать мне простой пример, который мог бы работать?

Я знаю, что мне нужно объявить тип TThread с помощью Create и Override Execute … и т. Д. Но с тех пор я потерялся…

Использование Delphi 7, SQL Server 2005 и ADO, Windows XP sp3.-

Спасибо.

Комментарии:

1. Вы пробовали много примеров, и у вас ничего не получилось. Итак, что бы дать вам другой пример? Вам следует задать более подробный вопрос. С чем у вас проблема? Кстати, этот учебник выглядит красиво: wiki.lazarus. freepascal.org/Multithreaded_Application_Tutorial

2. И еще одно, иногда лучшим решением (потому что это проще) просто вызвать Application . ProcessMessages время от времени внутри вашего трудоемкого процесса (например, просто для обновления некоторого индикатора выполнения или воспроизведения какой-либо другой анимации, показывающей пользователю, что приложение занято).

3. Вот пример, который я нашел полезным: многопоточные запросы к базе данных Delphi

4. ПРИВЕТ, adf88, я полностью согласен с тобой. Но проблема с примерами заключается в том, что я действительно не понимаю всего, что касается потоков, поэтому я подумал, что простой пример может сделать вещи более понятными для меня. В любом случае, я решил проблему с приложением. Processmessages, спасибо

Ответ №1:

Да, вы объявляете новый тип, который наследуется от TThread:

 TMyWorkerThread = class(TThread)
end;
 

Затем вы добавляете переопределение функции для Execute():

 TMyWorkerThread = class(TThread)
public
  procedure Execute; override;
end;
 

Эта процедура будет вызвана при запуске вашего потока. Он будет выполняться параллельно с вашей основной программой. Давайте напишем это.

 procedure TMyWorkerThread.Execute;
begin
  //Here we do work
   DoSomeWork();
   DoMoreWork();
  //When we exit the procedure, the thread ends.
  //So we don't exit until we're done.
end;
 

Как это использовать? Допустим, вы хотите начать выполнять работу, когда пользователь нажимает кнопку. Вы пишете обработчик OnClick:

 procedure TMainForm.Button1Click(Sender: TObject);
begin
  TMyWorkerThread.Create(false);
end;
 

Вот и все. После того, как пользователь нажимает кнопку, ваш поток запускается и продолжает выполнять все, что вы написали в Execute . Если пользователь снова нажмет на кнопку, запустится другой поток, а затем еще один — по одному при каждом нажатии. Все они будут выполняться параллельно, каждый из которых выполняет все, что написано в Execute(), а затем завершается.

Допустим, вы хотите проверить, закончена ли работа. Для этого вам нужно будет где-то сохранить ссылку на ваш поток:

 TMainForm = class(TForm)
{...skipped...}
public
  MyWorkerThread: TThread;
end;

procedure TMainForm.Button1Click(Sender: TObject);
begin
  //This time we make sure only one thread can be started.
  //If one thread have been started already, we don't start another.
  if MyWorkerThread<>nil then
    raise Exception.Create('One thread have already been started!');
  MyWorkerThread := TMyWorkerThread.Create(false);
end;

procedure TMainForm.Button2Click(Sender: TObject);
begin
  //If the work is not over yet, we display message informing the user we're still working
  if (MyWorkerThread<>nil) and (WaitForSingleObject(MyWorkerThread.Handle, 0)<>WAIT_OBJECT_0) then
    MessageBox(Self.Handle, pchar("The work is not yet done!"), pchar("Still running"), MB_OK);
end;
 

Как вы видите, мы проверяем, все ли еще выполняется поток, вызывая функцию Windows с именем WaitForSingleObject . Эта функция ожидает, пока поток не завершит работу или не истечет время ожидания, и поскольку мы указываем время ожидания 0, оно просто существует немедленно, если поток еще не завершен.

Комментарии:

1. Я знаю, что это просто пример кода, но комментарий о том, что произойдет, если пользователь нажмет кнопку более одного раза, был бы полезен.

2. Разве это «TThread» в последнем определении блока кода не должно быть «MyWorkerThread: TMyWorkerThread» вместо этого?

3. @ThatMarc: Идея здесь заключалась в том, что вам не нужно знать, какую конкретную работу выполняет TMyWorkerThread, чтобы знать, что поток завершен. Любой потомок TThread может быть проверен таким образом.

4. Спасибо за информацию. Я не пытался быть здесь умным, но я впервые имею дело с потоками и просто хотел убедиться, что я понял это правильно … 🙂 Спасибо за отличный пост bdw! Помогает лучше понять потоки в целом…

5. Кстати, есть ли какой-либо другой способ, который не полагается на функцию Windows WaitForSingleObject , чтобы ориентироваться и на другие платформы?

Ответ №2:

Вы можете найти много примеров в web of threads. Единственная особенность, если вы используете соединения ADO внутри потока, заключается в том, что вы не можете использовать одно и то же соединение.
Каждый поток должен создавать свое собственное соединение, в противном случае они равны (должны следовать тем же правилам, что и любой другой поток.)

Пример, который я использовал, это:

   TADOSQLThread = class(TThread)
  private
    FADOQ: TADOQuery;  // Internal query
    FSQL: string;      // SQL To execute
    FID: integer;      // Internal ID

  public
    constructor Create(CreateSuspended:Boolean; AConnString:String;
                       ASQL:string; IDThread:integer);
    destructor Destroy; override;
    procedure Execute(); override;

    property ID:integer read FID write FID;
    property SQL:string read FSQL write FSQL;
    property ADOQ:TADOQuery read FADOQ write FADOQ;
  end;
 

Конструктор Create переопределен и выглядит следующим образом:

 constructor TADOSQLThread.Create(CreateSuspended:Boolean; AConnString:String;
                                 ASQL:string; IDThread:integer);
begin

  inherited Create(CreateSuspended);

  // ini
  Self.FreeOnTerminate := False;

  // Create the Query
  FADOQ := TAdoquery.Create(nil);
  // assign connections
  FADOQ.ConnectionString := AConnString;
  FADOQ.SQL.Add(ASQL);
  Self.FID := IDThread;
  Self.FSQL:= ASQL;
end;
 

И метод execute очень прост:

 procedure TADOSQLThread.Execute();
begin

  inherited;

  try
    // Ejecutar la consulta
    Self.FADOQ.Open;
  except
    // Error al ejecutar
    ...Error treattement
  end;
end;
 

Чтобы запустить и создать поток, вы можете использовать код, подобный этому:

   //crear el Thread
  th := TADOSQLThread.Create(True, mmConnection.Lines.Text, ASQL, AId);
  // internal for me (for controled the number of active threads and limete it)
  inc(numThreads);
  // evento finalizacion
  th.OnTerminate := TerminateThread;
  // Ejecutarlo
  th.Resume;
 

Я создал метод TerminateThread, который получает управление потоками после их завершения. Единственное отличие от других потоков — проблема с подключением. Вы должны создавать новое соединение в каждом потоке, он не может использовать одни и те же ADOConnections с другими.
Я надеюсь, что этот пример будет полезен для вас.

С уважением