Подтвердить что ты не робот

MessageLoop внутри потока

Как я могу реализовать цикл сообщений в потоке с использованием OTL? Application.ProcessMessages; это то, что я использовал до сих пор, но это не очень безопасно использовать.

Спасибо

4b9b3361

Ответ 1

Вот как я вытягиваю сообщения из очереди потоков:

while GetMessage(Msg, 0, 0, 0) and not Terminated do begin
  Try
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  Except
    Application.HandleException(Self);
  End;
end;

Использование Application.ProcessMessages приведет к вытаскиванию сообщений очереди вызывающего потока. Но он не подходит для использования в цикле сообщений, потому что он не будет блокироваться. Вот почему вы используете GetMessage. Он блокирует, если очередь пуста. И Application.ProcessMessages также вызывает другие методы TApplication, которые не предназначены для потокобезопасности. Поэтому есть множество причин не называть его нитью, кроме основного потока.

Когда вам нужно завершить поток, я делаю это:

Terminate;
PostThreadMessage(ThreadID, WM_NULL, 0, 0);
//wake the thread so that it can notice that it has terminated

Ни один из них не является специфичным для OTL. Этот код предназначен для жизни в потоке TThread. Однако идеи могут быть переданы.


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

while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
  Try
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  Except
    Application.HandleException(Self);
  End;
end;

Ответ 2

Вы можете создать цикл, аналогичный циклу в Application.Run.

Вы можете вызвать PeekMessage, который проверяет наличие сообщения. PeekMessage проверяет очередь сообщений текущего потока, поэтому, если вы используете его в своем потоке, он проверяет очередь сообщений в потоке (на который вы можете отправлять сообщения с помощью PostThreadMessage).

Вместо PeekMessage вы также можете использовать GetMessage, который ждет, пока не будет получено сообщение. GetMessage возвращает 0 возвращает false. *)

когда он получает сообщение WM_QUIT, которое также является сигналом для завершения цикла сообщения. После этого рискованно вызвать GetMessage, так как он может не получить другое сообщение, и это может помешать вашему приложению нормально закрыться, поскольку оно блокируется.

Application.ProcessMessages действительно не очень безопасно, потому что он выполняет множество дополнительных функций, характерных для основного потока. Во-первых, он запускает Application.OnIdle, как только очередь сообщений пуста, что означает, что она вызывает это (проблема 1), когда очередь сообщений потока пуста и (проблема 2) в контексте потока, не позволяя VCL в этом случае.

* ) О возвращаемом значении GetMessage: я заметил, что Delphi реализует GetMessage как возвращающий LongBool. Однако фактическое возвращаемое значение является целым числом. Он возвращает 0 в случае WM_QUIT, -1 в случае ошибки и ненулевое значение в других случаях. Microsoft заявляет:

Поскольку возвращаемое значение может быть отличным от нуля, ноль или -1, избегайте использования кода, например это:

while (GetMessage( lpMsg, hWnd, 0, 0)) ...

К сожалению, вам нужно будет импортировать GetMessage под другим псевдонимом, чтобы использовать его правильно.