Тема не получает сообщения - PullRequest
1 голос
/ 08 марта 2009

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

procedure TMyThread.Execute;
begin
  while GetMessage(Msg, 0, 0, 0) and not Terminated do
  begin
    {thread message}
    if Msg.hwnd = 0 then
    begin
      ...
    end
    else
      DispatchMessage(Msg);
  end;
end;

Выполняя некоторые тесты с использованием моего приложения, я обнаружил, что функция GetMessage зависит от основного потока. Под этим я подразумеваю, что хотя основной поток выполняет некоторую работу, функция GetMessage в моем потоке не возвращает, даже если сообщение ожидает его получения (сообщение отправляется еще одним потоком с использованием функции PostThreadMessage: PostMessage ( MyThreadId, WM_MyMessage, 0, 0)).

Только когда основной поток завершает свою работу или вызывается метод Application.ProcessMessages, GetMessage возвращает результат, и мой поток начинает выполнять свою работу. Реализуя этот вид межпотоковой связи, я был уверен, что мои потоки будут работать независимо, и я никогда не ожидал, что получение сообщений, отправленных непосредственно в поток, будет зависеть от основного потока.

Выполняя тесты, я использовал функцию WaitForSingleObject в главном потоке, ожидая события в течение нескольких секунд. Это когда я заметил, что мой поток не делал никакой работы, даже если сообщения были отправлены ему другим потоком. Когда функция WaitForSingleObject наконец-то закончила ожидание и основной поток перестал работать, функция GetMessage в моем потоке вернулась.

Может кто-нибудь объяснить мне, почему это так работает? Есть ли обходной путь к этому? Я бы хотел, чтобы моя тема получала сообщения самостоятельно. Все мои темы созданы основным потоком. Может ли это быть причиной?

Заранее спасибо за помощь.

Мариуш.


Mghie, вы снова были абсолютно правы (вы, возможно, помните меня в последнее время с сообщениями). Как вы и предлагали, GetMessage немедленно возвращается, но поток фактически зависает при вызове метода главного окна:

procedure TMyThread.Execute;
begin
  while GetMessage(Msg, 0, 0, 0) and not Terminated do
  begin
    {thread message}
    if Msg.hwnd = 0 then
    begin
      ...
      if Assigned(FOnCommEventMethod) then
        FOnCommEventMethod(FCommEventsQueueItem);
      ...
    end
    else
      DispatchMessage(Msg);
  end;
end;

FOnCommEventMethod - это метод объекта, объявленный как 'процедура (EventMask: Cardinal) объекта;' (этот поток обрабатывает события последовательного порта). В этом случае FOnCommEventMethod была назначена процедура, принадлежащая к основному классу формы. Когда метод вызывается моим потоком, поток зависает, ожидая, пока основной поток завершит свою работу.

Как получилось? Как видите, я не использую метод Synchronize () для вызова этой процедуры. Поэтому я не ожидал, что мой поток будет синхронизироваться с основным потоком. Это происходит неявно? Кстати, я понимаю, что к любым компонентам графического интерфейса не должны обращаться никакие другие потоки, кроме основного, поэтому следует использовать метод Synchronize, но сейчас я делаю только несколько быстрых тестов.

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

Спасибо за вашу помощь. Если бы вы не помогли мне, я, вероятно, избавился бы от сообщений и использовал бы вместо этого события, и, наконец, заметил бы, что это не было причиной: -).

Ответы [ 3 ]

3 голосов
/ 08 марта 2009

Только когда основной поток завершает свою работу или вызывается метод Application.ProcessMessages, GetMessage возвращает значение, и мой поток начинает выполнять свою работу.

Я сомневаюсь, что это действительно то, что происходит. AFAIK два цикла сообщений должны быть независимы друг от друга, если вы не используете другие средства синхронизации потоков, такие как SendMessage () . Вы уверены, что поток действительно блокируется внутри GetMessage () , а не внутри Synchronize () (который использует SendMessage () для внутреннего использования)?

Выполняя тесты, я использовал функцию WaitForSingleObject в главном потоке, ожидая события в течение нескольких секунд.

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

2 голосов
/ 08 марта 2009

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

MSG msg;
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
SetEvent(messageQueueReady);
while (GetMessage(&msg, NULL, 0, 0)) 
{
    ...// do message processing here
}

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

Редактировать: Извините за пример на C, надеюсь, это нормально для вас.

0 голосов
/ 08 марта 2009

См. http://groups.google.com/group/borland.public.delphi.language.delphi.win32/browse_thread/thread/cf368834a606321b

Запрошенная дистилляция, исключающая множество мрачных деталей, в которых прячется дьявол: основная (или VCL) нить является особенной в Delphi, потому что многие вещи под капотом не безопасны для потоков, и он владеет ими всеми. Такие вещи, как цикл обработки сообщений, ожидают из-за вещей, которые синхронизируются с потоком VCL под капотом. У многих людей есть теории об этом. Лучше всего работать так, чтобы поток VCL был максимально легким и отзывчивым, чтобы минимизировать время ожидания. Все в значительной степени согласны с этим. У некоторых людей есть идеи о том, что может сработать, но другие думают, что они просто напрашиваются на неприятности.

...