web-dev-qa-db-ja.com

TaskCanceledExceptionが発生するのはなぜですか?

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

_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);
}
_

スレッドメソッドでは、例外をスローしませんが、タスクを開始する外部コードの_try-catch_ブロックでTaskCanceledExceptionを取得します。これが起こる理由と、この場合のtoken.ThrowIfCancellationRequested();の目的は何ですか。スレッドメソッドでtoken.ThrowIfCancellationRequested();を呼び出す場合にのみ、例外をスローする必要があると思います。

54
net_prog

競合状態の変化に直面しているので、これは予想される動作だと思います。

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

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

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

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

ここでの私の推測では、2つのタスクで.Start()を呼び出しているときに、CancellationTokenSource.Cancel()を呼び出す前に一方(または両方)が実際に起動しなかった可能性があります。タスクの開始とキャンセルの間に少なくとも3秒間待機すると、例外はスローされません。また、.Status両方のタスクのプロパティ。私が正しければ、.Statusプロパティは TaskStatus.Canceled 例外がスローされたときの少なくとも1つ。

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

24
Joshua