web-dev-qa-db-ja.com

C#-バックグラウンドワーカーのCancelAsync()が機能していませんか?

プロセスを中止したいのですが、中止できません。処理機能でバックグラウンドワーカーを使用しています。

_public void Init()
{
    bw = new BackgroundWorker();
    bw.WorkerSupportsCancellation = true;
    bw.DoWork += new DoWorkEventHandler(bw_DoWork);
    bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}

void bw_DoWork(object sender, DoWorkEventArgs e)
{
    if (bw.CancellationPending == true)
    {
        e.Cancel = true;
    }
    else
    {
        e.Result = abd();
    }
}

void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if(e.Cancelled)
    {
        lbltext.content="Canceled";
    }

    else
    {
        lbltext.content="Completed";
    }
}

private void btncan_Click(object sender, RoutedEventArgs e)
{
    bw.CancelAsync();
}

private void btnstart_Click(object sender, RoutedEventArgs e)
{
    bw.RunWorkerAsync();
}
_

このコードを使用してプロセスを中止することはできません。関数abd()は処理部分を実行し、結果を返します。

解決策を教えてください。

ありがとう。

12
Neel Bhasin

bw.CancelAsync()を呼び出すときは、CancellationPendingフラグをtrueに設定するだけです。デフォルトではキャンセルされません。保留中のキャンセルは手動で処理する必要があります。ただし、ボタンをクリックすると、次の3つのオプションが考えられるため、コードでそれを行うことはできません。

  • 長時間実行されているabd()メソッドは動作を終了し、キャンセルするものはありません
  • abd()は作業を開始し、バックグラウンドワーカーはブロックされます-abd()の結果を待ってから、実行を続行します-つまり、if-elseブロックを終了し、RunWorkerCompletedイベントを発生させます。
  • ほぼ不可能なオプション-あなたは軽いのと同じくらい速く、if-elseブロックが入る前にボタンをクリックします。 CancellationPendingよりもtrueになり、abd()は実行を開始しません

キャンセルを使用する場合は、実行時間の長いタスクをループで実行し、各ステップでキャンセルが保留されているかどうかを確認します。

void bw_DoWork(object sender, DoWorkEventArgs e)
{
    List<Foo> results = new List<Foo>();

    // any loop here - foreach, while
    for(int i = 0; i < steps_count; i++)
    {    
         // check status on each step
         if (bw.CancellationPending == true) 
         {
             e.Cancel = true;
             return; // abort work, if it's cancelled
         }

         results.Add(abd()); // add part of results
    }

    e.Result = results; // return all results
}
12

おそらくDoWorkは、CancelAsyncを呼び出す前に、ドキュメントに記載されているようにe.Cancelledは間違っている可能性があります。

ドキュメント これを言う

DoWorkイベントハンドラーのコードは、キャンセルリクエストが行われているときに作業を終了する可能性があり、ポーリングループでCancellationPendingがtrueに設定されていない可能性があることに注意してください。この場合、キャンセル要求が行われた場合でも、RunWorkerCompletedイベントハンドラーのSystem.ComponentModel.RunWorkerCompletedEventArgsのキャンセル済みフラグはtrueに設定されません。この状況は競合状態と呼ばれ、マルチスレッドプログラミングで一般的な懸念事項です。

1
Anirudha

次はどうですか?

While(!bw.CancellationPending)
{
   //do some work!
}
e.Cancel = true;
0
user2695458

Function abd()の処理に時間がかかりすぎて、途中で中止したいようです。

_private Thread _backgroundWorkerThread;

public void AbortBackgroundWorker()
{
    if(_backgroundWorkerThread != null)
    _backgroundWorkerThread.Abort();
}

void DoWork(object sender, DoWorkEventArgs e)
{
    try
    {
    _backgroundWorkerThread = Thread.CurrentThread;

    // call abd...

    }
    catch(ThreadAbortException)
    {
        // Do your clean up here.
    }
}
_

AbortBackgroundWorker()を_btncan_Click_イベントに入れます。しかし、これにはコストが伴います。これで、RunWorkerCompletedイベントは機能しなくなります。したがって、catchブロックで処理するか、独自のコードを作成する必要があります。

0
Suryanshu Singh