.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();
}