C# Taskの引数に使うCancellationTokenは何に使われているのか? | 株式会社リッカWebサイト

株式会社リッカ

BLOG
ブログ詳細

2018-06-18C# Taskの引数に使うCancellationTokenは何に使われているのか?

 

はじめに

Taskのキャンセル周りの実装で、ふと思いました。
CancellationTokenをTask開始のメソッドで指定できるけど、
ラムダの外で定義したtokenを使うんだから指定しなくてもよくない??
何か理由があるのだろうか??

 

指定する理由

色々探していたら同じ疑問を見つけました。
https://stackoverflow.com/questions/3712939/cancellation-token-in-task-constructor-why

  1. タスク開始前にトークンがキャンセルされてるかのチェック。タスクを開始するコストが減るから。
  2. タスクのボディーでも監視をしていて、例外を投げたトークンとボディーのトークンが一致するかを見ていて、同じだったら、タスクのステータスはCanceledになる。普通は、Faulted。

なるほど、指定すると少し挙動が変わったり、タスクのステータスが細かく設定されるところみたいですね。
と言うわけで、ちょいと検証してみましょう!!

 

検証

検証パターンと結果を書きます。ソースはいっぱいになるので、最後に貼り付けておきます。

VS2017で検証

パターン1.Task.Factory.StartNew()を使用する
パターン2.Task.Run()を使用する

A.正常にTaskを終了させる
B.Task実行前にキャンセルを行う
C.引数のCancellationTokenとTask内のCancellationTokenを同一のものを使用し、実行中にCancelを行う
D.引数のCancellationTokenとTask内のCancellationTokenを別のものにして、実行中にCancelを行う

1.Task.Factory.StartNew() 2.Task.Run()
A.正常にTaskを終了させる Task実行後+RanToComplation Task実行後+RanToComplation
B.Task実行前にキャンセルを行う Task実行せずCanceld Task実行せずCanceld
C.引数のCancellationTokenとTask内のCancellationTokenを同一のものを使用し、実行中にCancelを行う 例外を発生させCanceld 例外を発生させCanceld
D.引数のCancellationTokenとTask内のCancellationTokenを別のものにして、実行中にCancelを行う 例外発生しFaulted 例外を発生させCanceld

 

Task.Factory.StartNew()は言われていた結果通りになりました。
しかし、Task.Run()はタスクのステータスがFaultedに変わりませんでした。
ステータスを見るときには少し注意が必要ですね。

※て言うか、Dみたいな意味の分からない使い方はしないだろうし、メンバで持ってるCancellationTokenSourceのインスタンスが変わってしまうのも設計が悪そうだし…

 

TaskをCancelしてわかったこと

その他、キャンセルした時の挙動で分かったことも書いておきます。

キャンセルされたタスクのWaitを行うと例外(TaskCanceledException: A task was canceled.)が発生する。
そのため、キャンセルを前提としている場合は例外処理が必要。

キャンセルの反映が遅いので、DelayをかけないとWaitingToRunになってしまうことがあった。
処理にステータス判定をしている時はタイムラグに注意。

 

最後に

最初にまとまってたんですけど、今回はじっくり検証したのでブログにしました。
疑問に思った方の解決になれば幸いです。
そして、最後にテストコードを貼ります。

 

テストコード