6

以下のようなタスクのキャンセルに関する簡単な例を実行しようとしました

CancellationTokenSource tokenSource2 = new CancellationTokenSource();

CancellationToken token2 = tokenSource2.Token;


Task task2 = new Task(() =>
{
    for (int i = 0; i < int.MaxValue; i++)
    {
        token2.ThrowIfCancellationRequested();
        Thread.Sleep(100);
        Console.WriteLine("Task 2 - Int value {0}", i);
    }
}, token2);

task2.Start();

Console.WriteLine("Press any key to cancel the task");
Console.ReadLine();

tokenSource2.Cancel();
Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled);

が出力されると思ってい ましたが、"False"Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled);が出力されました。**"Task 2 cancelled? True"**

何がおこったか知ってますか?それは期待される動作ですか?ありがとう。

編集: キャンセル要求が呼び出される前にタスクが完了していないことを確認します。を追加しましたConsole.ReadLine()

4

3 に答える 3

5

まず、あなたはどういうIsCanceled意味か誤解しているのでしょうか?「これTaskはキャンセル待ちなので、まもなく完了するはずです」という意味ではなく、「これはキャンセルTaskされました。現在完了しています」という意味です。

それを誤解していなかった場合は、イベントのシーケンスが正確に何であるかを考えてください。何が起こるかこれは:

  1. ThrowIfCancellationRequested()が呼び出されますが、トークンはまだキャンセルされていないため、スローされません。
  2. Thread.Sleep()が呼び出されるため、実行中のスレッドがTaskスリープします。
  3. Cancel()と呼ばれます。
  4. IsCanceledチェックされます。のコードはTask、トークンがキャンセルされたことを認識する機会がなかったため、トークンはまだ実行されているため、をIsCanceled返しますfalse
  5. ThrowIfCancellationRequested()が再度呼び出され、今回はスローされ、実際にはがキャンセルされますTask

ISCanceledこれがあなたに戻ってきた理由falseです。を返したい場合は、チェックする前にtrueのようなものを追加するか、さらに良いことに、実際にが完了するのを待つことができます。Thread.Sleep(150)IsCanceledTask

于 2012-12-15T22:34:13.807 に答える
2

キャンセルを要求する前にタスクが終了しました。以下を参照してください。これにより、問題の解決方法がさらに明らかになる可能性があります。

ここから読むとhttp://social.msdn.microsoft.com/Forums/da-DK/parallelextensions/thread/9f88132a-f8bd-4885-ab63-645d7b6c2127タスクが実行される前に、タスクをキャンセルするためにトークンが使用されているようです「本当に」開始されましたが、キューに入れられた後です。

発生するようにスケジュールされているが、まだ開始されていないタスクをキャンセルする方法です。タスクが実行されると、それをキャンセルする唯一の方法は、メソッド内で独自のチェックを介して協調的に行うことです。これがないと、常にタスクを開始してから内部でチェックする必要があり、余分な不要なオーバーヘッドが大量に追加されます。

Task コンストラクターの Cancellation トークンから読み取ることもできます。

于 2012-12-15T21:20:48.193 に答える
2

これは答えではありませんが、コメントに書くには長すぎます

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = cancellationTokenSource.Token;

Task task = new Task(() =>
{
    for (int i = 0; i < int.MaxValue; i++)
    {
        cancellationToken.ThrowIfCancellationRequested();
        Console.WriteLine("Task 2 - Int value {0}", i);
    }
}, cancellationToken);

task.Start();

cancellationTokenSource.Cancel();
try
{
    task.Wait();
}
catch (AggregateException ae)
{
    if(ae.InnerExceptions.Single() is TaskCanceledException)
        Console.WriteLine("Caught TaskCanceledException");
    else
        Console.WriteLine("Did not catch canceled");
}
Console.WriteLine("Task 2 cancelled? {0}", task.IsCanceled);
  • 上記のコードは期待どおりの結果を出力しますが、'Task 2 - Int value {0}' の出力が得られなかったので、キャンセルする前に終了したとは思いません。
  • 私のサンプルでは、​​タスクをキャンセルすると例外がスローされることに注意してください
  • patterns for handling exceptions using tasks読んで良かったと思えるものもあります。
于 2012-12-15T21:34:21.360 に答える