7

INFINITE タイムアウトで WaitForSingleObject() を使用するアプリケーションを閉じる際に問題が発生しています。

全体像はこれです。アプリケーションがデバイスのウェイクアップ イベントを処理できるように、次のことを行っています。

イベントを次の方法で登録します。

CeRunAppAtEvent("\\\\.\\Notifications\\NamedEvents\\WakeupEvent",
    NOTIFICATION_EVENT_WAKEUP);

待機する新しいスレッドを開始します。

Thread waitForWakeThread = new Thread(new ThreadStart(WaitForWakeup));
waitForWakeThread.Start();

次に、ターゲット メソッドで次の操作を行います。

private void WaitForWakeup()
{
    IntPtr handle = CreateEvent(IntPtr.Zero, 0, 0, "WakeupEvent");
    while (true)
    {
        WaitForSingleObject(handle, INFINITE);
        MessageBox.Show("Wakey wakey");
    }
}

予想通り、WaitForSingleObject が待機し続け、アプリを適切に閉じることができない場合、アプリケーションを閉じようとするまで、これはすべて正常に機能します。一度に実行できるアプリのインスタンスは 1 つだけで、起動時にこれを確認します。デバイスがソフト リセットされるまで動作し続けるように見えます。

WaitForSingleObject が待機しているハンドルを強制終了して強制的に返す方法はありますか?

どうもありがとう。

4

4 に答える 4

19

代わりに WaitForMultipleObject を使用し、2 つのハンドルを渡します。既存のものと、「exit」などと呼ばれるイベント用のもの。アプリのシャットダウン中に、終了イベントで SetEvent を実行すると、WaitForMultipleObject が返され、スレッドを正常に終了させることができます。

どのハンドルがトリガーされたかに応じて適切な動作を行うには、WaitForMultipleObject の戻り値をオンにする必要があります。

また、スレッドをバックグラウンド スレッドに設定することもできます。これにより、メイン スレッドの終了時にアプリケーションがシャットダウンするのを防ぐことができます。

見る:

http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground.aspx

于 2011-05-17T15:23:56.260 に答える
2

これは私がすることです...

  1. CreateEvent を直接呼び出す代わりに、EventWaitHandle クラスを使用します。CeRunAppAtEvent 以外の Windows API を使用する必要はありません (API 呼び出しはコードを醜くします...)。これを最初に機能させます。
  2. スレッドを作成する前に、最初にフラグが設定されていない ManualResetEvent 変数を作成します。それを「TerminateEvent」と呼びます。
  3. WaitForSingleObject API 呼び出しを WaitHandle.WaitAny(WaitHandle[]) に置き換え、「TerminateEvent」を含む配列と、CeRunAppAtEvent 通知をラップする EventWaitHandle クラスを渡します。
  4. ループでは、WaitAny の戻り値を使用して、何をすべきかを判断できます。戻り値は、スレッドのブロックを解除した待機ハンドルの配列インデックスであるため、ループを続行するかどうかを判断できます。
  5. スレッドをきれいに終了するには、「TerminateEvent」で「Set」を呼び出してから、スレッドを「Join」して、スレッドが終了するのを待ちます。
于 2011-05-17T15:34:02.363 に答える
1

「予想通り、WaitForSingleObject が待機し続け、アプリを適切に閉じることができない場合に、アプリケーションを閉じようとするまで、これはすべて正常に機能します。」

スレッドが何をしているかに関係なく、どのアプリも閉じることができます。アプリ内の任意のスレッドから ExitProcess(0) を呼び出すと、API/sychro で INFINITE を待機しているスレッド、スリープしているスレッド、別のプロセッサで実行中のスレッドなどがあっても、アプリは終了します。OS は、実行されていないすべてのスレッドの状態を「二度と実行しない」に変更し、プロセッサ間ドライバーを使用して、実際にスレッド コードを実行している他のプロセッサをハード割り込みします。すべてのスレッドが停止すると、OS はハンドルやセグメントなどを解放し、アプリは存在しなくなります。

アプリが閉じているときに、開発者がスタックしているスレッドを「きれいに」シャットダウンしようとすると、問題が発生します。そう..

OnClose/OnCloseQuery ハンドラ、FormDestroy、またはデストラクタに TThread.WaitFor などがありますか? スレッドを確実に終了させる重大な理由がない場合は、コメントアウトしてください。

これにより、メイン フォームを閉じることができるため、コードは最終的に、赤い十字ボタンをクリックしてから取得しようとしていた ExitProcess() に到達します。

もちろん、自分で ExitProcess() を呼び出すこともできますが、これにより、データベース接続などの他のプロセスでリソースがリークしたままになる可能性があります。

「スレッドを停止しないと、クローズ時に 216/217 エラーが発生します」。これは、開発者が「残念な」Delphi スレッドの例に従い、セカンダリ スレッド フィールドとメイン スレッド フィールドの間でデータを直接交換することによってスレッドと通信するためによく発生します (例: TThread.synchronize)。アプリの実行中であっても、フォームが破棄されてスレッドが書き込みを試みているとき、またはスレッドが破棄されてメインスレッドのフォームがその上で呼び出しメソッドを試しています。スレッドと非同期に通信する方が、両方とも存続する queueing/PostMessaging オブジェクトを使用する方がはるかに安全です。スレッド/フォームで作成され、フォーム/スレッドで、または (スレッドセーフな) 初期化セクションで作成されたオブジェクトのプールによって解放されるオブジェクト。

「フォーム ハンドルは閉じているため無効ですが、スレッドがそれにメッセージを投稿しようとしています」。PostMessage が例外の場合は、スレッドを終了します。より良い方法は、上記のアプローチに似ています。すべてのフォームよりも長生きするウィンドウにのみメッセージを投稿します。すべてのスレッドがポストに使用する 1 つの const メッセージ番号のみを処理する単純な WndProc を使用して、初期化セクションに作成します。wParam を使用して TwinControlインスタンスを渡すことができますスレッドが通信しようとしているもの (通常はフォーム変数) であり、lParam は通信中のオブジェクトを渡します。スレッドからメッセージを取得すると、WndProc は渡された TwinControl で「Peform」を呼び出し、TwinControl はメッセージ ハンドラで通信オブジェクトを取得します。たとえば、単純なグローバル ブール値「AppClosing」は、シャットダウン中に自分自身を解放する TwinControls で Peform() を呼び出す WndProc を停止できます。このアプローチは、OS が別のハンドルでフォーム ウィンドウを再作成するときに発生する問題も回避します。Delphi フォーム ハンドルは使用されず、Windows は初期化で作成された単純なフォームのハンドルを再作成/変更しません。

私は何十年もこれらのアプローチに従ってきましたが、数十のスレッドがキューにオブジェクトを投げかけているアプリでも、シャットダウンの問題は発生しません.

Rgds、マーティン

于 2011-05-18T09:07:12.807 に答える
0

もちろん、これを解決するための好ましい方法は、、または複数の基準( 、など)WaitForMultipleObjectsを待機できるその他の適切な関数を使用することです。WaitForMultipleObjectsMsgWaitForMultipleObjects

ただし、使用する関数を制御できない場合は、これを解決するためのトリッキーな方法がいくつかあります。hack任意のモジュールのインポートテーブルをメモリ内で変更することにより、システムDLLからインポートされた関数を使用できます。WaitForMultipleObjectskernel32.dllからエクスポートされるので、問題ありません。このテクニックを使用すると、関数の呼び出し元を自分の手にリダイレクトでき、そこでを使用できるようになりますWaitForMultipleObjects

于 2011-05-17T15:25:22.577 に答える