質問
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()メソッドを使用してこのプロシージャを呼び出すことはありません。したがって、自分のスレッドがメインスレッドと同期することを期待していません。それは暗黙的に起こりますか?ところで、GUIコンポーネントはメインスレッド以外の他のスレッドからはアクセスできないため、Synchronizeメソッドを使用する必要があることを理解していますが、現在は簡単なテストのみを行っています。
WaitForSingleObjectの件名に戻って、私はそれを使うべきではないことを知っていますが、問題に気付いた(偶然)おかげでテストに過ぎませんでした。
ご協力ありがとうございます。あなたが私を助けなかったなら、私はおそらくメッセージングを取り除き、代わりにイベントを使用するでしょう、そして最終的に私はそれが理由ではないことに気付くでしょう:-)。
解決
メインスレッドが処理を終了するか、Application.ProcessMessagesメソッドが呼び出された場合にのみ、GetMessageが戻り、スレッドが処理を開始します。
これが実際に起こっていることだとは思わない。知っている限り、2つのメッセージループは、 SendMessage()などの他のスレッド同期手段を使用しない限り、互いに独立している必要があります。スレッドが実際に Synchronize()( SendMessage()を内部で使用している)ではなく、 GetMessage()内でブロックしていることを確認してください)?
テストの実行メインスレッドでWaitForSingleObject関数を使用し、数秒間イベントを待機しました。
メインスレッドで、タイムアウトが100ミリ秒より長い WaitForSingleObject()を使用しないでください。GUIが遅くなります。実際、メインスレッドではまったく使用しないことをお勧めします。これは単にポーリングを行うためです。代わりに、ワーカースレッドからメッセージを投稿してください。
他のヒント
スレッドのメッセージキューを作成できます。スレッドで次のコードを実行するだけです。
MSG msg;
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
SetEvent(messageQueueReady);
while (GetMessage(&msg, NULL, 0, 0))
{
...// do message processing here
}
PeekMessageの呼び出しにより、OSはスレッドの新しいメッセージキューを強制的に作成します。これを必ず同期する必要があります。つまり、このスレッドにメッセージを投稿する前に(たとえば、PostThreadMessageを介して)呼び出しが成功するまで待機する必要があります(WaitForSingleObjectを介してなど)。これが、上の例でSetEvent APIの呼び出しがある理由です。
編集: Cのサンプルで申し訳ありませんが、それで問題ないことを願っています。
要求された蒸留。悪魔が隠している多くの厄介な詳細を省きます。メイン(またはVCL)スレッドは、Delphiで特別なものです。メッセージループのようなものは、内部のVCLスレッドと同期するために待機します。多くの人がこれについての理論を持っています。最適に動作するように思われるのは、VCLスレッドをできるだけ軽く/応答性を保ち、待機時間を最小限に抑えることです。誰もがこれにほぼ同意しています。うまくいくかもしれない他のことについてのアイデアを持っている人もいますが、他の人は単にトラブルを求めていると思います。