型 | プロパティ | 内容 |
---|---|---|
bool | IsCancellationRequested | 取り消し (キャンセル) が要求されているかどうか |
CancellationToken | Token | このCancellationTokenSourceに関連付けられているCancellationToken |
メソッド | 機能 |
---|---|
Cancel() | キャンセルを要求できる |
CancelAfter(Int32) | キャンセルするまでの時間を設定できる |
キャンセルを要求する側はCancellationTokenSource.Cancel()を呼び出し、別スレッドの側はそれのCancellationToken.IsCancellationRequestedでキャンセルの要求を検出し処理を中断します。
CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; Task.Run(() => { if (token.IsCancellationRequested) { // キャンセルを要求されたならば、処理を抜ける break; } }); // キャンセルを要求する tokenSource.Cancel();
一度キャンセルを要求したCancellationTokenSourceはその要求を取り消せないため、再度CancellationTokenSourceが必要なときはそのインスタンスを再生成します。
CancellationTokenSourceへの最後の参照を解放する前に、CancellationTokenSource.Dispose()を呼びます。さもなくばガベージ コレクタがFinalize()を呼ぶまで、そのリソースは解放されません。 Dispose() - CancellationTokenSource.Dispose Method (System.Threading) | Microsoft Learn c# - When to dispose CancellationTokenSource? - Stack Overflow
ただしDispose()の後にCancel()を呼ぶとObjectDisposedExceptionが投げられるため、それへの対処が必要です。
キャンセルしたことを呼び出し側へ通知したいならば、OperationCanceledExceptionを投げることでタスクを終了させます。そうするとStatusプロパティはTaskStatus.Canceledとなり、TaskContinuationOptions.OnlyOnCanceledを指定したContinueWith()で継続タスクを実行できます。
OperationCanceledExceptionはタスクのスレッドから投げられないと捕捉されず、継続タスクも実行されません。
CancellationToken.ThrowIfCancellationRequested()を呼び出してキャンセルさせる方法もありますが、これは
if (token.IsCancellationRequested) throw new OperationCanceledException(token);
とすることと同義です。CancellationToken.ThrowIfCancellationRequested Method (System.Threading) | Microsoft Learn
OperationCanceledExceptionが投げられるTaskは、そのコンストラクタでCancellationTokenを渡します。これはキャンセルできたことをTask.IsCanceledで検証するときにも有用ですが、これらが不要ならば渡す必要はありません。c# - Cancellation token in Task constructor: why? - Stack Overflow
public Task ( Action action, System.Threading.CancellationToken cancellationToken );Task(Action, CancellationToken) - Task Constructor (System.Threading.Tasks) | Microsoft Learn
CancellationTokenをTaskのコンストラクタで渡さないとキャンセルと見なされずStatusはFaultedとなり、継続タスクはOnlyOnCanceledではなくOnlyOnFaultedが実行されます。
CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; Task task = new Task(() => { token.ThrowIfCancellationRequested(); // token.IsCancellationRequested が true ならば、 // OperationCanceledExceptionが投げられる }, token); // CancellationTokenを渡さねばならない task.ContinueWith((_task) => { // キャンセルされたときに、ここが実行される }, TaskContinuationOptions.OnlyOnCanceled);
CancellationTokenSourceを作成するときに時間を指定すると、一定時間後に自動でキャンセルが要求されるようにできます。
指定の時間でキャンセルが要求されるだけで、その時間でキャンセルされるわけではありません。
public CancellationTokenSource (int millisecondsDelay);CancellationTokenSource(Int32) - CancellationTokenSource Constructor (System.Threading) | Microsoft Learn
コンストラクタの呼び出し時点から秒読みが開始され、指定の時間が経過するとToken.IsCancellationRequestedがtrueに設定されます。この時間は、CancellationTokenSource.CancelAfter()で後から変更できます。
int millisecondsDelay = 100; CancellationTokenSource tokenSource = new CancellationTokenSource(millisecondsDelay); CancellationToken token = tokenSource.Token; Task task = new Task(() => { if (token.IsCancellationRequested) { } });
連続する要求に対し、最後の1つだけを処理する方法を考えます。
CancellationTokenSource tokenSource = null; void Method() { CancellationTokenSource newTokenSource = new CancellationTokenSource(); CancellationTokenSource oldTokenSource = Interlocked.Exchange(ref tokenSource, newTokenSource); if (oldTokenSource != null) { oldTokenSource.Cancel(); // キャンセルを要求する } try { // 待機する await Task.Delay(1000, newTokenSource.Token); // キャンセルが要求されたならばTaskCanceledExceptionが投げられる // 何らかの処理 } catch (TaskCanceledException) { } }