キャンセルされたタスクが完了するまで待つ方法は?質問する

キャンセルされたタスクが完了するまで待つ方法は?質問する

.NET 4.0 での並列プログラミングに関しては、明らかに何をやっているのかわかりません。単純な Windows アプリがあり、これは、無意味な作業 (1 ~ 1000 の数値の出力) を実行するタスクを開始します。長時間実行されるプロセスをシミュレートするために、途中でかなりの一時停止を設定します。この長時間の一時停止が行われている間に [停止] ボタンをクリックすると、そのイベント ハンドラーは CancellationTokenSource の Cancel メソッドを呼び出します。キャンセルされたタスクが現在の反復処理を完了するまで、[停止] ボタンのイベント ハンドラーでそれ以上の処理 (この場合は、メッセージの出力) を実行したくありません。どうすればよいでしょうか。[停止] ボタンのイベント ハンドラーで Task.WaitAll などを使用しようとしましたが、処理されない AggregateException がスローされるだけです。上記のように実行した場合の問題を説明するコードを以下に示します。

  private Task t;
  private CancellationTokenSource cts;

  public Form1()
  {
     InitializeComponent();
  }

  private void startButton_Click(object sender, EventArgs e)
  {
     statusTextBox.Text = "Output started.";

     // Create the cancellation token source.
     cts = new CancellationTokenSource();

     // Create the cancellation token.
     CancellationToken ct = cts.Token;

     // Create & start worker task.
     t = Task.Factory.StartNew(() => DoWork(ct), ct);
  }

  private void DoWork(CancellationToken ct)
  {
     for (int i = 1; i <= 1000; i++)
     {
        ct.ThrowIfCancellationRequested();

        Thread.Sleep(10);  // Slow down for text box outout.
        outputTextBox.Invoke((Action)(() => outputTextBox.Text = i + Environment.NewLine));

        if (i == 500)
        {
           Thread.Sleep(5000);
        }
     }
  }

  private void stopButton_Click(object sender, EventArgs e)
  {
     cts.Cancel();

     Task.WaitAll(t);  // this doesn't work :-(

     statusTextBox.Text = "Output ended.";
  }

  private void exitButton_Click(object sender, EventArgs e)
  {
     this.Close();
  }

これに関してご助力いただければ幸いです。よろしくお願いします。

ベストアンサー1

Task.Waitこれは単一のタスクなので、通常は ではなく ( )を使用しますWaitAll。その後、例外を適切に処理しました。

private void stopButton_Click(object sender, EventArgs e)
{
    cts.Cancel();
    try
    {
        t.Wait();  // This will throw
    }
    catch (AggregateException ae)
    {
       ae.Handle<OperationCanceledException>(ce => true);
    }

    statusTextBox.Text = "Output ended.";
}

をキャンセルするとTask、 はOperationCanceledExceptionにラップされAggregateException、 を呼び出すかWait()、タスクの を取得しようとするとすぐに がスローされますResult( の場合Task<T>)。


純粋に参考までに - これは、特にここで行っていることを考慮すると、C# 5 によって物事が簡素化される場所の 1 つです。新しい非同期サポートを使用すると、次のように記述できます。

// No need for "t" variable anymore 
// private Task t;


private async void startButton_Click(object sender, EventArgs e)
{
   statusTextBox.Text = "Output started.";

   // Create the cancellation token source.
   cts = new CancellationTokenSource();

   try
   {
      // Create & start worker task.
      await Task.Run(() => DoWork(cts.Token));
      statusTextBox.Text = "Output ended.";
   }
   catch(OperationCanceledException ce) 
   {
      // Note that we get "normal" exception handling
      statusTextBox.Text = "Operation canceled.";
   }
}

private void stopButton_Click(object sender, EventArgs e)
{
   // Just cancel the source - nothing else required here
   cts.Cancel();
}

おすすめ記事