При разработке многопоточных приложений на Delphi часто возникают вопросы, связанные с корректным завершением потоков. Одной из таких проблем является ситуация, когда при закрытии приложения некоторые потоки не завершаются должным образом. В данной статье мы рассмотрим типичную проблему, связанную с использованием сообщений WM_QUIT для завершения потоков, и предложим решение, основанное на лучших практиках программирования на Object Pascal.
Описание проблемы
Рассмотрим типичную ситуацию, когда в приложении на Delphi присутствуют несколько потоков:
Основной поток.
Два вспомогательных потока с циклом обработки сообщений (как показано ниже).
n рабочих потоков, выполняющих простые циклы с задержкой.
Проблема заключается в том, что при закрытии приложения рабочие потоки корректно завершаются, в то время как один из вспомогательных потоков "зависает" (не завершается) после отправки ему сообщения 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;
Подтвержденный ответ
Из контекста вопроса видно, что проблема заключается в неправильном использовании механизма обработки сообщений в потоках. Создание скрытого окна для получения сообщений не является необходимым, так как потоки уже имеют свою систему сообщений. Сообщение, отправленное в fHandle, не обрабатывается функцией GetMessage, так как она работает с сообщениями, предназначенными для текущего потока.
Для корректного завершения потока следует использовать функцию PostThreadMessage для отправки сообщений в поток, а в самом потоке использовать GetMessage с указанием текущего сообщения. Важно начать цикл обработки сообщений с PeekMessage, чтобы убедиться, что сообщения, отправленные во время инициализации потока, не теряются.
Альтернативный ответ
Для отправки сообщений в поток следует использовать PostThreadMessage, а не создавать скрытое окно с помощью AllocateHwnd.
Для обеспечения создания очереди сообщений перед отправкой в поток можно использовать цикл с задержкой, пока сообщение не будет успешно отправлено.
Для завершения потока можно определить собственное сообщение, например, idExitMessage = WM_USER + 777.
В функции WaitForSingleObject можно указать дескриптор потока, что исключает необходимость в создании отдельного события.
Цикл обработки сообщений в потоке должен проверять, не получено ли сообщение завершения.
Пример исправленного кода
procedure ThreadProcFQM;
var
Msg: TMsg;
const
idExitMessage = WM_USER + 777;
begin
// Force Message Queue Creation
PeekMessage(Msg, 0, WM_USER, WM_USER, PM_NOREMOVE);
while GetMessage(Msg, 0, 0, 0) and (Msg.Message <> idExitMessage) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
procedure TFQM.Stop;
begin
PostThreadMessage(ThreadID, idExitMessage, 0, 0);
WaitForSingleObject(ThreadHandle, INFINITE);
end;
Заключение
При работе с многопоточностью в Delphi важно правильно обращаться с сообщениями и потоковыми очередями. Использование PostThreadMessage и PeekMessage позволяет корректно управлять потоками и их завершением. Следуя этим рекомендациям, можно избежать многих распространенных проблем при разработке многопоточных приложений.
Проблема заключается в том, что при закрытии приложения на Delphi с использованием сообщений WM_QUIT некоторые потоки могут не завершаться из-за неправильного использования механизма обработки сообщений в потоках.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.