4

DoWork で BackGroundWorker をキャンセルした後、CancellationPending は true ですが、RunWorkerCompleted になると、CancellationPending は false です。何を間違えたのかわからない?

static BackgroundWorker b1;

static void Main(string[] args)
{
    b1=new BackgroundWorker();
    b1.DoWork += new DoWorkEventHandler(work1);
    b1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completed);
    b1.WorkerSupportsCancellation = true;
    b1.RunWorkerAsync("Hellow");
    Console.ReadLine();
}

private static void completed(object sender, RunWorkerCompletedEventArgs e)
{
    if (((BackgroundWorker)sender).CancellationPending)
        Console.WriteLine("Canceled!");
    else
        Console.WriteLine("Result:" + e.Result);//it goes here every time
}

private static void work1(object sender, DoWorkEventArgs e)
{
    ((BackgroundWorker)sender).CancelAsync();
    if (((BackgroundWorker)sender).CancellationPending)
    {
        e.Cancel = true;
    }
}

ところで、DoWork で発生したエラーを RunWorkerCompletedEventArgs.Error に追加してユーザーに知らせるにはどうすればよいでしょうか?

4

2 に答える 2

8

はい、BackgroundWorker クラスは RunWorkerCompleted イベントを発生させる前に CancellationPending プロパティを false に設定します。ワーカーが実際にキャンセルされたかどうか。

これは非常に意図的なものであり、スレッドを使用するときに常に存在する厄介なトラップに陥るのを防ぎます。スレッドを使用するコードは、「スレッドの競合」と呼ばれる一種のバグが原因で、ランダムに予測できない動作をすることがよくあります。これは非常に一般的な種類のバグであり、デバッグが非常に困難です。

BGW がこれを行わなかった場合、意図したアプローチで簡単に問題が発生する可能性があるのは、CancellationPending が true に設定されていることを確認したときにワーカーがキャンセルされたと想定することです。しかし、それは錯覚です。キャンセルされた場合と正常に完了した場合の違いはわかりません。まれに、ワーカーが完了する前に CancelAsync() を呼び出す場合があります。ワーカーは、CancellationPending フラグが true に設定されていることを確認する機会さえありません。DoWork イベント ハンドラー メソッドの最後のビットを完了するのに忙しかったのです。これはスレッド化の競合です。ワーカーは呼び出しよりも先に競合し、正常に完了しました。

このバグを回避する適切なハンドシェイクは、ワーカーが CancellationPending プロパティが true に設定されていることを確認したときに e.Cancel を true に設定することです。そしてもちろん、それがしていることを止めます。RunWorkerCompleted イベント ハンドラの e.Cancelled プロパティは e.Cancel のコピーです。そのため、ワーカーがキャンセル要求を見たかどうかをコードで確実に判断できるようになりました。

于 2012-07-29T15:59:55.630 に答える
6

CancellationPendingプロパティは、バックグラウンド操作(work1メソッド)で使用するためのものだと思います。バックグラウンド操作のキャンセルを要求したことをバックグラウンドワーカーに通知します。RunWorkerCompletedイベントが呼び出されると、バックグラウンドワーカーが要求をキャンセルする作業を実行したため、キャンセルは保留されなくなります。

編集:RunWorkerCompletedEventArgsには、バックグラウンド操作がキャンセルされたかどうかを通知するCanceledプロパティがあります。

DoWorkメソッド(この場合はwork1)から例外をスローした場合、その例外はBackgroundWorkerによってキャッチされ、RunWorkerCompletedEventArgsのErrorプロパティに入力される必要があります。

于 2012-07-29T14:33:15.243 に答える