特定のSystem.Threading.Tasks.Task
コンストラクターは、パラメーターとしてCancellationToken
を取ります。
CancellationTokenSource source = new CancellationTokenSource();
Task t = new Task (/* method */, source.Token);
これについて私を困惑させているのは、insideからメソッド本体が渡されたトークンを実際に取得する方法がないことです(たとえば、Task.CurrentTask.CancellationToken
のようなものはありません)。トークンは、状態オブジェクトなどの他のメカニズムを通じて提供されるか、ラムダでキャプチャされる必要があります。
それでは、コンストラクタでキャンセルトークンを提供することは、どのような目的に役立ちますか?
このトークンをTaskコンストラクターに渡すと、トークンがこのタスクに関連付けられます。
これには2つの主な利点があります。
- タスクの実行が開始される前にトークンのキャンセルが要求された場合、タスクは実行されません。
Running
に移行するのではなく、すぐにCanceled
に移行します。とにかく実行中にタスクがキャンセルされた場合、これによりタスクの実行コストが回避されます。- タスクの本体もキャンセルトークンを監視しており、そのトークンを含む
OperationCanceledException
をスローする場合(これはThrowIfCancellationRequested
が行うことです)、タスクがOperationCanceledException
を検出すると、OperationCanceledException
のトークンは、タスクのトークンと一致します。存在する場合、その例外は協調キャンセルの確認応答と見なされ、タスクは(フォールト状態ではなく)キャンセル状態に移行します。
コンストラクターは、トークンを内部でキャンセル処理に使用します。コードがトークンへのアクセスを希望する場合は、トークンを自分に渡す責任があります。 CodePlexのMicrosoft .NETでの並列プログラミングの本 を読むことを強くお勧めします。
本のCTSの使用例:
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
Task myTask = Task.Factory.StartNew(() =>
{
for (...)
{
token.ThrowIfCancellationRequested();
// Body of for loop.
}
}, token);
// ... elsewhere ...
cts.Cancel();
多くの人が考えるように、キャンセルは単純なケースではありません。微妙な点のいくつかは、msdnのこのブログ投稿で説明されています。
例えば:
Parallel Extensionsや他のシステムの特定の状況では、ユーザーによる明示的なキャンセルが原因ではないため、ブロックされたメソッドを起動する必要があります。たとえば、コレクションが空であるために1つのスレッドがblockingCollection.Take()でブロックされ、その後別のスレッドがblockingCollection.CompleteAdding()を呼び出す場合、最初の呼び出しはウェイクアップし、不正な使用法を表すInvalidOperationExceptionをスローする必要があります。
http://blogs.msdn.com/b/pfxteam/archive/2009/06/22/9791840.aspx