5

ワーカー スレッドから定期的にイベントを発生させる外部コンポーネントを使用しています。私のイベント ハンドラーでは、Dispatcher を使用して、メイン スレッドでいくつかのメソッドを呼び出します。これはうまく機能します...

private void HandleXYZ(object sender, EventArgs e)
{
    ...
    if(OnTrigger != null)
        dispatcher.Invoke(OnTrigger, new TimeSpan(0, 0, 1), e);
}

ただし、プログラムがシャットダウンし、外部コンポーネントが Dispose() を実行すると、プログラム がハングすることがあります (タスク マネージャーでのみ確認および強制終了できます)。

何が起こっているのかを見ると、「コンポーネント」はイベントがメインスレッドに戻るのを待っているように見えます(Dispose()メソッドにとどまります)が、ワーカースレッドはディスパッチャーが言及された呼び出しを呼び出すのを待っていますメイン スレッド (dispatcher.Invoke-line でハングします)。

今のところ、Invoke にタイムアウトを追加することでシャットダウンの問題を解決しました。このようなことを行うためのよりクリーンな方法はありますか? シャットダウンする前に、メイン スレッドが他のスレッドからのジョブに時間を費やすように強制することはできますか?

シャットダウンする前にイベントを「切断」しようとしましたが、プログラムがシャットダウンを開始したときにディスパッチャーが既に待機しているため、役に立ちません...

PS: 外部コンポーネントとは、ここではソース コードにアクセスできないことを意味します...

4

2 に答える 2

6

はい、これはデッドロックの一般的な原因です。ディスパッチャがディスパッチャ ループを終了したためにハングし、Invoke リクエストに応答しなくなります。簡単な解決策は、代わりに BeginInvoke を使用することです。呼び出しターゲットの実行が完了するのを待ちません。もう 1 つの手っ取り早い方法は、ワーカー スレッドの IsBackground プロパティを True に設定して、CLR によって強制終了されるようにすることです。

これらは簡単な修正であり、うまくいく可能性があります。確かにあなたの開発マシンでは、それでもまだうまくいかないかもしれないというしつこい気持ちがあるなら、あなたは正しいです.デッドロックやスレッドの競合を観察しないことは、それらが存在しないことを証明しません. 完全に安全に行うための「良い」方法が 2 つあります。

  • ワーカー スレッドが終了し、イベントを発生させることができなくなったこと を確認するまで、メイン スレッドを終了させないでください。この回答はパターンを示しています。

  • Environment.Exit() でプログラムを強制終了します。これは非常に大雑把ですが、非常に効果的であり、UI スレッドが第 2 市民にすぎない、スレッド化されたプログラムを使用している場合にのみ利用できる大ハンマーです。これは適切なアプローチとして聞こえるかもしれませんが、新しい C++ 言語標準では、プログラムを終了するためのサポートされている方法に昇格されています。詳細については、この回答を参照してください。クリーンアップ関数を登録できるようにする方法に注意してください。たとえば、AppDomain.ProcessExit イベントで同様のことを行う必要があります。これを行う前に、最初の箇条書きに集中してください。

于 2012-07-19T10:14:24.723 に答える
1

イベント サブスクリプションに関しては、特定のオブジェクトがもう必要ないことがわかっている場合は、それらをクリーンアップすることをお勧めします。そうしないと、メモリ リークが発生する危険があります。また、弱いイベント パターン(MSDN) も参照してください。

デッドロック自体に関しては、コードを知らなくても、推測することしかできません。

HandleXYZ()が犯人だとは思いません。むしろあなたの実装を確認したいと思いますIDisposable()MSDN のドキュメントを見て、実装と比較してください。

実装のどこかで、のタイミングに依存するいくつかのメソッド呼び出しが行われていると思いますがGarbageCollector、これは不確定です。

于 2012-07-19T10:00:21.497 に答える