0

ほとんどの場合、このコードは機能するので、競合状態を考えています。結果クラスは不変ですが、そのクラスに問題があるとは思いません。

public Result GetResult()
{
    using (var waitHandle = new ManualResetEvent(false))
    {
        Result result = null;

        var completedHandler = new WorkCompletedEventHandler((o, e) =>
        {
            result = e.Result;

            // somehow waitHandle is closed, thus exception occurs here
            waitHandle.Set();
        });
        try
        {
            this.worker.Completed += completedHandler;

            // starts working on separate thread
            // when done, this.worker invokes its Completed event
            this.worker.RunWork();

            waitHandle.WaitOne();

            return new WorkResult(result);
        }
        finally
        {
            this.worker.Completed -= completedHandler;
        }
    }
}

編集: 申し訳ありませんが、GetResult() メソッドを呼び出す直前に this.worker.RunWork() を呼び出していませんでした。Completed イベントが 2 回発生したにもかかわらず、waitHandle.Set()の前に waitHandle が閉じられた理由はわかりませんが、これにより (時々) 同じジョブが 2 回実行されたようです。これは IO 作業をまったく損なうものではありません (結果は正しかったです。コードを変更して手動でwaitHandleを閉じた後)。

したがって、質問が完全ではなかったとしても、 Iridiumの回答は(正しい回答ではないにしても)最も近い回答になるはずです。

4

3 に答える 3

2

あなたが与えたコードには特に問題があるようには見えません。これは、あなたが示していないコードに問題を引き起こしている可能性があることを示唆しています。worker使用しているのはコードベースの一部であると想定しています (.NET BCL のようなものではなくBackgroundWorker)。

たとえば、同じワーカーが複数のスレッドから繰り返し使用される場合 (または、同じ作業に対して複数Completed回発生する可能性があるバグがある場合)、ワーカーがイベント ハンドラーを呼び出すために "通常の" 手段を使用すると、すなわち:

var handler = Completed;
if (handler != null)
{
    handler(...);
}

が句のvar handler = Completed;実行される (したがって、がイベントから切り離される前) が、ブロックが終了した後 (したがって、が破棄された後) に呼び出されるインスタンスを持つことができます。その後、イベント ハンドラーが破棄された後に実行され、表示されている例外がスローされます。finallycompletedHandlerCompletedhandler(...)using(...)ManualResetEventwaitHandle

于 2012-12-31T16:19:26.720 に答える
1

投稿されたコードからこれが失敗する明確な理由はありません。ただし、スタック トレースは表示されず、Completed イベントを発生させるロジックも表示されないため、これをデバッグする機会はほとんどありません。任意に、イベントが複数回発生する場合、この種の競合の問題が確実に発生します。

厄介なスレッドの問題はデバッグが難しく、スレッドの競合はマイクロ秒単位で発生する問題です。デバッグしようとするだけで、レースが消えてしまう可能性があります。または、問題が発生する頻度が非常に低いため、問題を発見する見込みがほとんどないため、試みを正当化することはできません。

このような問題では、競合を診断するためにロギングが必要になることがよくあります。必ず軽量のロギング方法を選択してください。ロギング自体によってタイミングが変更され、レースが発生しなくなる可能性があります。

最後になりましたが、ここでスレッドを使用しても意味がないことに注意してください。RunWork() によって開始されたスレッドによって実行されるコードを直接呼び出すことによって、まったく同じ結果が得られます。オーバーヘッドと頭痛のマイナス。

于 2012-12-31T16:13:16.900 に答える
0

コードを使用しない場合、指定した行で例外がスローされません...本当に必要な場合は、それを処分する適切な場所を見つける必要があります。

public Result GetResult()
{
    var waitHandle = new ManualResetEvent(false);

        Result result = null;

        var completedHandler = new WorkCompletedEventHandler((o, e) =>
        {
            result = e.Result;

            // somehow waitHandle is closed, thus exception occurs here
            waitHandle.Set();
            waitHandle.Dispose();
        });
        try
        {
            this.worker.Completed += completedHandler;

            // starts working on separate thread
            // when done, this.worker invokes its Completed event
            this.worker.RunWork();

            waitHandle.WaitOne();

            return new WorkResult(result);
        }
        finally
        {
            this.worker.Completed -= completedHandler;
        }
}
于 2012-12-31T15:40:51.603 に答える