Цикл сообщений Delphi Multi-Threading - PullRequest
8 голосов
/ 23 октября 2008

Мое приложение имеет несколько потоков: 1) Основная тема 2) 2 вспомогательных потока (каждый с циклом сообщений, как показано ниже), используемый TFQM 3) n рабочих потоков (простой цикл, содержащий Sleep ())

Моя проблема в том, что когда я закрываю свое приложение, рабочим потокам удается правильно выйти, но 1 из 2 вспомогательных потоков зависает (никогда не выходит), когда я запускаю WM_QUIT, чтобы закрыть их.


procedure ThreadProcFQM(P: Integer); stdcall;
var
  Msg: TMsg;
 _FQM: TFQM;
begin
  _FQM := Ptr(P);
  try
    _FQM.fHandle := AllocateHwnd(_FQM.WndProc);

    while GetMessage(Msg, 0, 0, 0) do
    begin
      TranslateMessage(Msg);
      DispatchMessage(Msg);
    end;

  finally
    DeallocateHWnd(_FQM.fHandle);
    SetEvent(_FQM.hTerminated);
  end;
end;

procedure TFQM.Stop;
begin
  PostMessage(fHandle, WM_QUIT, 0, 0);

  WaitForSingleObject(hTerminated, INFINITE);
  if hThread <> INVALID_HANDLE_VALUE then
  begin
    CloseHandle(hThread);
    hThread := INVALID_HANDLE_VALUE;
  end;
end;

Ответы [ 3 ]

11 голосов
/ 23 октября 2008

Если я могу указать на несколько проблем в вашем коде ...

1) Вы не проверяете вывод AllocateHwnd. Да, скорее всего, это никогда не подведет, но все же ...

2) AllocateHwnd belogs OUT of try..finally! Если это не удается, DeallocateHwnd не должен вызываться.

3) AllocateHwnd не является потокобезопасным. Если вы вызываете его из нескольких потоков одновременно, вы можете столкнуться с проблемами. Читать дальше.

Как сказал Дэви, используйте MsgWaitForMultipleObjects вместо создания скрытого окна сообщения. Затем используйте PostThreadMessage для отправки сообщений в цепочку.

Если я могу поставить здесь заглушку для совершенно бесплатного продукта - используйте вместо этого OmniThreadLibrary . Гораздо проще, чем связываться напрямую с сообщениями Windows.

9 голосов
/ 23 октября 2008

У меня была такая же проблема, и я обнаружил, что не должен создавать скрытое окно только для получения сообщений. Темы уже имеют систему сообщений.

Я думаю, что вы создаете дескриптор окна и сохраняете его в fHandle, но GetMessage проверяет цикл сообщений вашего потока. Поэтому сообщение PostMessage (fHandle, WM_QUIT, 0, 0); getmesssage никогда не получает.

Вы можете публиковать сообщения в своей теме, используя PostThreadMessage, а в теме вы используете GetMessage (CurrentMessage, 0, 0, 0). Единственное важное отличие состоит в том, что вы должны запустить цикл сообщений из своей ветки, вызвав

PeekMessage(CurrentMessage, 0, WM_USER, WM_USER, PM_NOREMOVE);

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

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

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

7 голосов
/ 23 октября 2008

1) Вам не нужен AllocateHwnd в вашей теме. Первый вызов GetMessage создаст отдельную очередь сообщений для этого потока. Но чтобы отправить сообщение в ветку, вы должны использовать функцию PostThreadMessage.

Имейте в виду, что в момент вызова PostThreadMessage очередь все еще не может быть создана. Я обычно использую конструкцию:

while not PostThreadMessage(ThreadID, idStartMessage, 0, 0) do
  Sleep(1);

для обеспечения создания очереди сообщений.

2) Для завершения цикла я задаю собственное сообщение:

  idExitMessage = WM_USER + 777; // you are free to use your own constant here

3) Нет необходимости в отдельном событии, потому что вы можете передать дескриптор потока Функция WaitForSingleObject. Итак, ваш код может выглядеть так:

  PostThreadMessage(ThreadID, idExitMessage, 0, 0);
  WaitForSingleObject(ThreadHandle, INFINITE);

Учтите, что ThreadID и ThreadHandle - это разные значения.

4) Итак, ваш ThreadProc будет выглядеть так:

procedure ThreadProcFQM; stdcall;
var
  Msg: TMsg;
begin
  while GetMessage(Msg, 0, 0, 0) 
    and (Msg.Message <> idExitMessage) do
  begin
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...