Task④

タスクのキャンセル

タスクに渡したラムダ式内でOperationCanceledExceptionをスローすることでタスクをキャンセルできます。
キャンセルされたタスクのTask.StatusはCanceledになります。

var task = Task.Run(() => throw new OperationCanceledException());

try
{
    // タスクは即時キャンセルされるわけでなく、終了を待機する必要があります
    task.Wait();
}
catch (AggregateException)
{

}
finally
{
    // 出力結果は「task status : Canceled」になります
    Console.WriteLine($"task status : {task.Status}");
}

CancellationToken

CancellationTokenSource・CencellationTokenを使用することで、外部からタスクのキャンセルをコントロールできます。

var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var task = Task.Run(
    () =>
    {
        while (!token.IsCancellationRequested)
        {
            Thread.Sleep(200);
            Console.WriteLine($"processing...");
        }
    });

tokenSource.CancelAfter(1000);

try
{
    task.Wait();
}
catch (AggregateException)
{

}
finally
{
    Console.WriteLine($"task status : {task.Status}");
}

CancellationTokenSource.Cancel()・CancellationTokenSource.CancelAfter()を実行することで、CancellationTokenSource.Tokenから取得されたトークン及びそのコピーにキャンセルが通知されます。
キャンセルが通知された後、CancellationTokenSource.IsCancellationRequested・CancellationToken.IsCancellationRequestedの値がTrueになるため、
タスクに渡したラムダ式内で上記プロパティ値をモニターし、Trueの場合に終了するような仕組みを組み込むことで、外部からタスクをキャンセルすることができます。
ただし、この方法はフラグで終了判定を行っているに過ぎないため、Task.StatusはRanToCompletionになります。
Task.StatusをCanceledにしたい場合、OperationCanceledExceptionをスローすることで実現できます。

上記サンプルのケースでは適当なローカル変数でも代用可能なので、CancellationTokenのメリットが薄いですが、タスク内で他クラスのメソッドを呼ぶなど、メソッド外に制御が移る場合は有用です。
呼び出し先に順次CancellationTokenを渡していくことで、すべてのメソッドでキャンセル状態をモニターできるようになります。

また、タスクにCancellationTokenを渡すことで、もう少し細かな制御が可能になります。

var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var task = Task.Run(
    () =>
    {
        while (true)
        {
            token.ThrowIfCancellationRequested();

            Thread.Sleep(200);
            Console.WriteLine($"processing...");
        }
    },
    token);

tokenSource.CancelAfter(1000);

try
{
    task.Wait();
}
catch (AggregateException)
{

}
finally
{
    Console.WriteLine($"task status : {task.Status}");
}

CancellationToken.ThrowIfCancellationRequested()はOperationCanceledExceptionをスローします。
また、以下条件を満たす場合にTask.StatusをCanceledにし、満たさない場合はFaultedにします。

  • CancellationToken.IsCancellationRequestedがTrue
  • 自身とタスクに渡されたトークンが同一

ちなみに、上記サンプルでCancelAfter()のパラメーターを0にした場合、即時キャンセル通知が行われます。
タスク実行開始前であればラムダ式は評価されずに終了し、Task.StatusはCenceledとなります。