web-dev-qa-db-ja.com

タスクコンストラクターのキャンセルトークン:なぜですか?

特定のSystem.Threading.Tasks.Taskコンストラクターは、パラメーターとしてCancellationTokenを取ります。

CancellationTokenSource source = new CancellationTokenSource();
Task t = new Task (/* method */, source.Token);

これについて私を困惑させているのは、insideからメソッド本体が渡されたトークンを実際に取得する方法がないことです(たとえば、Task.CurrentTask.CancellationTokenのようなものはありません)。トークンは、状態オブジェクトなどの他のメカニズムを通じて提供されるか、ラムダでキャプチャされる必要があります。

それでは、コンストラクタでキャンセルトークンを提供することは、どのような目的に役立ちますか?

212
Colin

このトークンをTaskコンストラクターに渡すと、トークンがこのタスクに関連付けられます。

引用 MSDNからのStephen Toubの回答

これには2つの主な利点があります。

  1. タスクの実行が開始される前にトークンのキャンセルが要求された場合、タスクは実行されません。 Runningに移行するのではなく、すぐにCanceledに移行します。とにかく実行中にタスクがキャンセルされた場合、これによりタスクの実行コストが回避されます。
  2. タスクの本体もキャンセルトークンを監視しており、そのトークンを含むOperationCanceledExceptionをスローする場合(これはThrowIfCancellationRequestedが行うことです)、タスクがOperationCanceledExceptionを検出すると、 OperationCanceledExceptionのトークンは、タスクのトークンと一致します。存在する場合、その例外は協調キャンセルの確認応答と見なされ、タスクは(フォールト状態ではなく)キャンセル状態に移行します。
245
Max Galkin

コンストラクターは、トークンを内部でキャンセル処理に使用します。コードがトークンへのアクセスを希望する場合は、トークンを自分に渡す責任があります。 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();
28
user7116

多くの人が考えるように、キャンセルは単純なケースではありません。微妙な点のいくつかは、msdnのこのブログ投稿で説明されています。

例えば:

Parallel Extensionsや他のシステムの特定の状況では、ユーザーによる明示的なキャンセルが原因ではないため、ブロックされたメソッドを起動する必要があります。たとえば、コレクションが空であるために1つのスレッドがblockingCollection.Take()でブロックされ、その後別のスレッドがblockingCollection.CompleteAdding()を呼び出す場合、最初の呼び出しはウェイクアップし、不正な使用法を表すInvalidOperationExceptionをスローする必要があります。

http://blogs.msdn.com/b/pfxteam/archive/2009/06/22/9791840.aspx

7
x0n