今回はタスクをキャンセルする方法について見ていきます。以前ループの取り消しでも触れましたが、.NET Framework 4ではキャンセルトークンを用いた統一的なキャンセル手法が提供されています。そして、タスクもそれを使ってキャンセルできるようにサポートされています。
タスクの実行を取り消す
まず、次のサンプルを見てください。Task Cancel Sample (1)
- using System;
- using System.Threading;
- using System.Threading.Tasks;
- namespace ConsoleApplication
- {
- class Program
- {
- static void Main()
- {
- var source = new CancellationTokenSource();
- var task = new Task(() => Console.WriteLine("タスクは実行されました。"), source.Token);
- task.Start();
- source.Cancel();
- try
- {
- task.Wait();
- }
- catch (AggregateException exception)
- {
- foreach (var inner in exception.InnerExceptions)
- {
- Console.WriteLine(inner.Message);
- Console.WriteLine("Type : {0}", inner.GetType());
- }
- }
- }
- }
- }
- //----- 結果
- /*
- タスクが取り消されました。
- Type : System.Threading.Tasks.TaskCanceledException
- */
上記のサンプルでは、タスクを開始するように指示していますが、結果としてタスクは実行されず、キャンセルされています。一体何が起こっているのでしょうか?ポイントは、CancellationTokenSource.CancelメソッドをTask.Startメソッドの直後に呼び出しているところです。内部では大体次のようになっているはずです。
- タスクとCancellationTokenをコンストラクタで関連付ける
- Startメソッドの呼び出しにより、既定のタスクスケジューラーにタスクを登録する
- タスクスケジューラーがタスクを開始する前にCancelメソッドが実行される
- タスクスケジューラーはタスクに関連付いているキャンセルトークンを確認する
- キャンセルが要求されていることが確認できたため、タスクの開始を取り消す
- タスクが保持する例外にTaskCanceledExceptionを登録する
- Waitメソッド呼び出し時、タスクが例外を保持しているのでスローする
実行途中でのキャンセル
タスクの実行中にキャンセルする方法も紹介します。こちらの方法はループの取り消しで説明した内容とほぼ同様です。タスク実行中にタスクがキャンセルされたかどうかをポーリングで監視し、キャンセルが要求されたらOperationCanceledExceptionをスローします。タスクの実行自体が取り消されたわけではないので、先程とは例外の型が異なることに注意してください。Task Cancel Sample (2)
- using System;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- namespace ConsoleApplication
- {
- class Program
- {
- static void Main()
- {
- var source = new CancellationTokenSource();
- var token = source.Token;
- var task = new Task(() =>
- {
- Console.WriteLine("Task : Begin");
- foreach (var item in Enumerable.Range(0, 10))
- {
- token.ThrowIfCancellationRequested();
- Thread.Sleep(100);
- }
- Console.WriteLine("Task : End");
- });
- task.Start();
- Thread.Sleep(300); //--- タスクが実行されるようにする
- source.Cancel();
- try
- {
- task.Wait();
- }
- catch (AggregateException exception)
- {
- foreach (var inner in exception.InnerExceptions)
- {
- Console.WriteLine(inner.Message);
- Console.WriteLine("Type : {0}", inner.GetType());
- }
- }
- }
- }
- }
- //----- 結果
- /*
- Task : Begin
- 操作はキャンセルされました。
- Type : System.OperationCanceledException
- */