昨日、タスク(TPL)を紹介されたばかりなので、タスクの使い方を理解するために、サンプルプロジェクトを少しやってみました。
私のサンプルプロジェクトは、プログレスバーのインクリメントを開始するスタートボタンでセットアップされています。タスクをキャンセルするための2番目のボタン。TaskContinuationOptions.OnlyOnRanToCompletionを使用した継続が呼び出されたときに報告するテキストボックスと、TaskContinuationOptions.OnlyOnCanceledを使用した継続が呼び出されたときに報告するテキストボックス。
タスクを作成して実行することはできますが、TaskContinuationOptions.OnlyOnCanceledフラグを使用して継続を起動できるようにタスクをキャンセルすると、問題が発生します。
次のようにタスクを作成します。
private void StartTask()
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
Task task = null;
task = Task.Factory.StartNew(() => DoWork(tokenSource), tokenSource.Token);
//A list<CancellationTokenSource> so that I can cancel the task when clicking a button on the UI Thread.
MyTasks.Add(tokenSource);
Task completed = task.ContinueWith(result => TaskCompleted(), TaskContinuationOptions.OnlyOnRanToCompletion);
Task canceled = task.ContinueWith(result => TaskCanceled(), TaskContinuationOptions.OnlyOnCanceled);
}
次のようにタスクをキャンセルします。
private void CancelTasks()
{
foreach (CancellationTokenSource tokenSource in MyTasks)
{
tokenSource.Cancel();
}
}
私のワーカー機能は次のとおりです。
private void DoWork(CancellationTokenSource tokenSource)
{
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke(new Action(() => DoWork(tokenSource)));
return;
}
try
{
bool dowork = true;
while (dowork)
{
tokenSource.Token.ThrowIfCancellationRequested();
if (progressBar1.Value == progressBar1.Maximum)
{
dowork = false;
}
Thread.Sleep(1000);
progressBar1.PerformStep();
Application.DoEvents();
}
countCompleted++;
}
catch (OperationCanceledException)
{
}
}
私が読んだ他の投稿では、tokenSource.Token.ThrowIfCancellationRequested()がTaskContinuationOptions.OnlyOnCanceledによって評価される条件を設定するものであることが示唆されています。
私が見た例のどれも、以下の使用を含んでいません:
catch (OperationCanceledException)
{
}
ただし、これがないと、tokenSource.Cancel();を呼び出すとプログラムが停止します。
現状では、tokenSource.Cancel()を呼び出すと、TaskContinuationOptions.OnlyOnCanceledではなく、TaskContinuationOptions.OnlyOnRanToCompletionを使用した継続が実行されます。
明らかに私はこれを正しく行っていません。
編集:
さらに読んでみると、次のようなコメントが見つかりました。
"catch(OperationCanceledException){}は、タスクのステータスをキャンセルではなくRanToCompletionとして設定します"
したがって、catch(OperationCanceledException){}を削除すると、タスクのステータスをキャンセルに設定できますが、プログラムはtokenSource.Token.ThrowIfCancellationRequested();で中断します。しかし、その後休憩を続けると、TaskContinuationOptions.OnlyOnCanceledを使用した継続タスクが実行されます。これは良いことです。
しかし、プログラムを中断せずに、タスクのステータスをキャンセルに設定したまま、tokenSource.Token.ThrowIfCancellationRequested()を呼び出すにはどうすればよいですか?