Цикл сообщений потока зависает в Delphi

У меня есть простая программа Delphi, над которой я работаю, в которой я пытаюсь использовать многопоточность, чтобы отделить функциональность программы от ее графического интерфейса и сохранить отклик графического интерфейса во время более длительных задач и т. д. По сути, у меня есть «контроллер» TThread и «представление» TForm. Представление знает дескриптор контроллера, который он использует для отправки сообщений контроллера через PostThreadMessage. В прошлом у меня не было проблем с использованием такой модели для форм, которые не являются основной формой, но по какой-то причине, когда я пытаюсь использовать эту модель для основной формы, цикл сообщений потока просто завершается.

Вот мой код для цикла сообщений потоков:

procedure TController.Execute;
var
  Msg : TMsg;
begin
  while not Terminated do begin
    if (Integer(GetMessage(Msg, hwnd(0), 0, 0)) = -1) then begin
      Synchronize(Terminate);
    end;

    TranslateMessage(Msg);
    DispatchMessage(Msg);

    case Msg.message of
      // ...call different methods based on message
    end;
  end;
end;

Чтобы настроить контроллер, я делаю это:

Controller := TController.Create(true); // Create suspended
Controller.FreeOnTerminate := True;
Controller.Resume;

Для обработки сообщений основной формы я пробовал использовать как Application.Run, так и следующий цикл (сразу после Controller.Resume)

while not Application.Terminated do begin
  Application.ProcessMessages;
end;

Я застрял здесь - любая помощь будет принята с благодарностью.


person Erik Westenbroek    schedule 22.03.2010    source источник
comment
Добро пожаловать в Stack Overflow, erikjw! Надеюсь, вы найдете хороший ответ. Быстрое замечание, однако. Ваш код становится труднее читать, когда вы ставите begin в той же строке, что и do или then перед ним. Это считается хорошим стилем в C, но в Delphi большинство из нас предпочитает помещать его в отдельную строку, чтобы мы могли визуально выстроить пары begin и end.   -  person Mason Wheeler    schedule 23.03.2010
comment
Спасибо за все ответы. Я пробовал каждый из них по отдельности, а также несколько разных комбинаций, и у меня все еще есть проблемы. Когда я выполняю отладку и прохожу код в TController.Execute, как только я перешагиваю через GetMessage, независимо от того, что я передаю как hwnd, цикл просто не проходит. Он не выполняет никаких операторов после цикла, он просто необъяснимо останавливается. И что еще больше сбивает с толку, так это то, что я не вижу признаков того, что сам поток действительно умер.   -  person Erik Westenbroek    schedule 23.03.2010
comment
Вы пытались удалить Synchronise()? Если цикл обработки сообщений не запущен, он может просто выйти из строя. Кроме того, какую версию Delphi вы используете?   -  person Nat    schedule 23.03.2010
comment
Я полностью с тобой не согласен, @Mason. begin в отдельной строке — это визуальный шум. Один только отступ говорит все, что нужно. Спрячьте шум в конце строки, чтобы он не мешал.   -  person Rob Kennedy    schedule 23.03.2010
comment
Эрик, из твоих утверждений непонятно, но ты же знаешь, что GetMessage блокирует, верно? Он больше ничего не будет выполнять в вашем цикле, пока вы не вызовете PostThreadMessage.   -  person Zoë Peterson    schedule 23.03.2010
comment
Ага! Я не знал, что GetMessage заблокирован. Думаю, мне следует искать ошибки в других местах, а не только в цикле сообщений. Спасибо!   -  person Erik Westenbroek    schedule 23.03.2010


Ответы (2)


Я проверил ваш код в основном как есть, и он работал нормально. Попробуйте добавить вызов GetLastError после того, как GetMessage вернет -1, чтобы увидеть, в чем проблема.

Из кода не совсем ясно, создаете ли вы окна в потоке контроллера, но если нет, я бы предложил передать -1 вместо 0 в качестве HWND для GetMessage и удалить вызовы TranslateMessage/DispatchMessage, поскольку оператор case который следует за ними, должен обрабатывать любые сообщения, которые вы получаете.

Кроме того, вам не нужно выполнять «Синхронизировать (Завершить)» при ошибке. Terminate просто устанавливает логическое значение «Terminated» в значение true, поэтому вам не нужно его синхронизировать, и вы можете так же легко использовать «Break», чтобы выйти из цикла с тем же эффектом.

person Zoë Peterson    schedule 22.03.2010

Где «конец» цикла while? Я думаю, что вы пропали без вести и конец. Так что, возможно (в зависимости от фактического кода), вы застряли в бесконечном цикле while not Terminated do на одном операторе.

person Chris Thornton    schedule 22.03.2010
comment
Это моя ошибка, я исправлю это в вопросе - цикл охватывает почти всю функцию. - person Erik Westenbroek; 23.03.2010