232

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

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

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

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

4

4 に答える 4

263

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

MSDNからのStephenToubの回答の引用:

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

  1. Task実行開始前にトークンにキャンセルが要求された場合、はTask実行されません。に移行するのではなく、 Runningすぐにに移行しCanceledます。これにより、実行中にタスクがキャンセルされた場合のタスクの実行コストを回避できます。
  2. タスクの本体もキャンセルトークンを監視していて、OperationCanceledExceptionそのトークンを含むものをスローする場合(これが実行されThrowIfCancellationRequestedます)、タスクはそれを認識すると、のトークンがタスクのトークンと一致するOperationCanceledExceptionかどうかを確認します。OperationCanceledExceptionその場合、その例外は、 (状態ではなく)協調的なキャンセルと状態へのTask遷移の確認と見なされます。CanceledFaulted
于 2010-09-14T21:38:23.220 に答える
27

コンストラクターは、トークンを使用して、内部でキャンセル処理を行います。あなたのコードがトークンへのアクセスを希望する場合、あなたはそれをあなた自身に渡す責任があります。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();
于 2010-09-14T21:32:38.453 に答える
7

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

例えば:

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

並列拡張でのキャンセル

于 2010-09-14T21:15:15.117 に答える
6

これは、 MaxGalkinによって受け入れられた回答の2つのポイントを示すコード例です。

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("*********************************************************************");
        Console.WriteLine("* Start canceled task, don't pass token to constructor");
        Console.WriteLine("*********************************************************************");
        StartCanceledTaskTest(false);
        Console.WriteLine();

        Console.WriteLine("*********************************************************************");
        Console.WriteLine("* Start canceled task, pass token to constructor");
        Console.WriteLine("*********************************************************************");
        StartCanceledTaskTest(true);
        Console.WriteLine();

        Console.WriteLine("*********************************************************************");
        Console.WriteLine("* Throw if cancellation requested, don't pass token to constructor");
        Console.WriteLine("*********************************************************************");
        ThrowIfCancellationRequestedTest(false);
        Console.WriteLine();

        Console.WriteLine("*********************************************************************");
        Console.WriteLine("* Throw if cancellation requested, pass token to constructor");
        Console.WriteLine("*********************************************************************");
        ThrowIfCancellationRequestedTest(true);
        Console.WriteLine();

        Console.WriteLine();
        Console.WriteLine("Test Completed!!!");
        Console.ReadKey();
    }

    static void StartCanceledTaskTest(bool passTokenToConstructor)
    {
        Console.WriteLine("Creating task");
        CancellationTokenSource tokenSource = new CancellationTokenSource();
        Task task = null;
        if (passTokenToConstructor)
        {
            task = new Task(() => TaskWork(tokenSource.Token, false), tokenSource.Token);

        }
        else
        {
            task = new Task(() => TaskWork(tokenSource.Token, false));
        }

        Console.WriteLine("Canceling task");
        tokenSource.Cancel();

        try
        {
            Console.WriteLine("Starting task");
            task.Start();
            task.Wait();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception: {0}", ex.Message);
            if (ex.InnerException != null)
            {
                Console.WriteLine("InnerException: {0}", ex.InnerException.Message);
            }
        }

        Console.WriteLine("Task.Status: {0}", task.Status);
    }

    static void ThrowIfCancellationRequestedTest(bool passTokenToConstructor)
    {
        Console.WriteLine("Creating task");
        CancellationTokenSource tokenSource = new CancellationTokenSource();
        Task task = null;
        if (passTokenToConstructor)
        {
            task = new Task(() => TaskWork(tokenSource.Token, true), tokenSource.Token);

        }
        else
        {
            task = new Task(() => TaskWork(tokenSource.Token, true));
        }

        try
        {
            Console.WriteLine("Starting task");
            task.Start();
            Thread.Sleep(100);

            Console.WriteLine("Canceling task");
            tokenSource.Cancel();
            task.Wait();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception: {0}", ex.Message);
            if (ex.InnerException != null)
            {
                Console.WriteLine("InnerException: {0}", ex.InnerException.Message);
            }
        }

        Console.WriteLine("Task.Status: {0}", task.Status);
    }

    static void TaskWork(CancellationToken token, bool throwException)
    {
        int loopCount = 0;

        while (true)
        {
            loopCount++;
            Console.WriteLine("Task: loop count {0}", loopCount);

            token.WaitHandle.WaitOne(50);
            if (token.IsCancellationRequested)
            {
                Console.WriteLine("Task: cancellation requested");
                if (throwException)
                {
                    token.ThrowIfCancellationRequested();
                }

                break;
            }
        }
    }
}

出力:

*********************************************************************
* Start canceled task, don't pass token to constructor
*********************************************************************
Creating task
Canceling task
Starting task
Task: loop count 1
Task: cancellation requested
Task.Status: RanToCompletion

*********************************************************************
* Start canceled task, pass token to constructor
*********************************************************************
Creating task
Canceling task
Starting task
Exception: Start may not be called on a task that has completed.
Task.Status: Canceled

*********************************************************************
* Throw if cancellation requested, don't pass token to constructor
*********************************************************************
Creating task
Starting task
Task: loop count 1
Task: loop count 2
Canceling task
Task: cancellation requested
Exception: One or more errors occurred.
InnerException: The operation was canceled.
Task.Status: Faulted

*********************************************************************
* Throw if cancellation requested, pass token to constructor
*********************************************************************
Creating task
Starting task
Task: loop count 1
Task: loop count 2
Canceling task
Task: cancellation requested
Exception: One or more errors occurred.
InnerException: A task was canceled.
Task.Status: Canceled


Test Completed!!!
于 2020-02-18T11:35:55.547 に答える