73

次のテストコードがあります。

void Button_Click(object sender, RoutedEventArgs e)
{
    var source = new CancellationTokenSource();

    var tsk1 = new Task(() => Thread1(source.Token), source.Token);
    var tsk2 = new Task(() => Thread2(source.Token), source.Token);

    tsk1.Start();
    tsk2.Start();

    source.Cancel();

    try
    {
        Task.WaitAll(new[] {tsk1, tsk2});
    }
    catch (Exception ex)
    {
        // here exception is caught
    }
}

void Thread1(CancellationToken token)
{
    Thread.Sleep(2000);

    // If the following line is enabled, the result is the same.
    // token.ThrowIfCancellationRequested();
}

void Thread2(CancellationToken token)
{
    Thread.Sleep(3000);
}

スレッド メソッドでは例外をスローしませんが、タスクを開始する外部コードのブロックにTaskCanceledException入ります。try-catchなぜこれが起こるのかtoken.ThrowIfCancellationRequested();、この場合の目的は何ですか。token.ThrowIfCancellationRequested();スレッドメソッドを呼び出した場合にのみ、例外がスローされるべきだと思います。

4

1 に答える 1

40

競合状態のバリエーションに遭遇しているため、これは予想される動作だと思います。

方法: タスクとその子をキャンセルするから:

呼び出しスレッドは強制的にタスクを終了しません。キャンセルが要求されたことを通知するだけです。タスクが既に実行されている場合、ユーザー デリゲートは要求に気づき、適切に応答する必要があります。タスクの実行前にキャンセルが要求された場合、ユーザー デリゲートは実行されず、タスク オブジェクトはCanceled状態に遷移します。

およびタスクのキャンセルから:

[...] デリゲートから戻るだけで操作を終了できます。多くのシナリオでは、これで十分です。ただし、この方法で「キャンセル」されたタスク インスタンスは、RanToCompletion状態ではなく状態に遷移しCanceledます。

ここでの私の経験に基づいた推測では.Start()、2 つのタスクを呼び出しているときに、1 つ (または両方) が実際に.Cancel()CancellationTokenSource. タスクの開始とキャンセルの間に少なくとも 3 秒待機すると、例外はスローされません。.Statusまた、両方のタスクのプロパティを確認できます。私が正しければ、例外がスローされたときに、プロパティはそれらの少なくとも 1 つを.Status読み取る必要があります。TaskStatus.Canceled

新しいスレッドを開始してTaskも、新しいスレッドが作成されるとは限りません。何が新しいスレッドを取得し、何が実行のために単にキューに入れられるかを決定するのは TPL です。

于 2013-03-03T05:21:08.137 に答える