52

わかりました、私の質問は本当に簡単です。このコードがスローしないのはなぜTaskCancelledExceptionですか?

static void Main()
{
    var v = Task.Run(() =>
    {
        Thread.Sleep(1000);
        return 10;
    }, new CancellationTokenSource(500).Token).Result;

    Console.WriteLine(v); // this outputs 10 - instead of throwing error.
    Console.Read();
}

しかし、これは機能します

static void Main()
{
    var v = Task.Run(() =>
    {
        Thread.Sleep(1000);
        return 10;
    }, new CancellationToken(true).Token).Result;

    Console.WriteLine(v); // this one throws
    Console.Read();
}
4

4 に答える 4

45

マネージ スレッドでのキャンセル:

キャンセルは協調的であり、リスナーに強制されません。リスナーは、キャンセル要求に応じて正常に終了する方法を決定します。

Task.Runメソッドにアクセスしてキャンセルを実装するためのコードをメソッド内に記述しなかったCancellationTokenため、キャンセルのリクエストを事実上無視して、完了するまで実行しました。

于 2014-03-25T14:40:24.400 に答える
33

実行中のタスクのキャンセルと、実行予定のタスクのキャンセルには違いがあります。

Task.Run メソッドの呼び出し後、タスクはスケジュールされているだけで、おそらくまだ実行されていません。

キャンセルをサポートするオーバーロードの Task.Run(..., CancellationToken) ファミリを使用すると、タスクが実行される直前にキャンセル トークンがチェックされます。この時点でキャンセル トークンの IsCancellationRequested が true に設定されている場合、タイプ TaskCanceledException の例外がスローされます。

タスクが既に実行されている場合、ThrowIfCancellationRequested メソッドを呼び出すか、単に OperationCanceledException をスローするのはタスクの責任です。

MSDN によると、これは次の場合の便利な方法です。

if (token.IsCancellationRequested) throw new OperationCanceledException(token);

この 2 つのケースでは、異なる種類の例外が使用されていることに注意してください。

catch (TaskCanceledException ex)
{
    // Task was canceled before running.
}
catch (OperationCanceledException ex)
{
    // Task was canceled while running.
}

TaskCanceledExceptionから派生することにも注意してください。そのため、型に対して 1 つの句OperationCanceledExceptionのみを使用できます。catchOperationCanceledException

catch (OperationCanceledException ex)
{
    if (ex is TaskCanceledException)
        // Task was canceled before running.
    // Task was canceled while running.
}
于 2015-11-10T19:26:17.840 に答える
23

ThrowIfCancellationRequested()CancellationToken オブジェクトからメソッドを呼び出していないためだと思います。このように、タスクのキャンセルの要求を無視しています。

次のようにする必要があります。

void Main()
{
    var ct = new CancellationTokenSource(500).Token;
     var v = 
     Task.Run(() =>
    {
        Thread.Sleep(1000);
        ct.ThrowIfCancellationRequested();
        return 10;
    }, ct).Result;

    Console.WriteLine(v); //now a TaskCanceledException is thrown.
    Console.Read();
}

コードの 2 番目のバリアントは、Canceled状態が true に設定されたトークンを既に初期化しているため、機能します。確かに、ここで述べたように:

If canceled is true, both CanBeCanceled and IsCancellationRequested will be true

キャンセルはすでに要求されておりTaskCanceledException、実際にタスクを開始することなく、すぐに例外がスローされます。

于 2014-03-25T14:40:43.340 に答える
5

Thread.Sleep の代わりにトークンで Task.Delay を使用する別の実装。

 static void Main(string[] args)
    {
        var task = GetValueWithTimeout(1000);
        Console.WriteLine(task.Result);
        Console.ReadLine();
    }

    static async Task<int> GetValueWithTimeout(int milliseconds)
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        CancellationToken token = cts.Token;
        cts.CancelAfter(milliseconds);
        token.ThrowIfCancellationRequested();

        var workerTask = Task.Run(async () =>
        {
            await Task.Delay(3500, token);
            return 10;
        }, token);

        try
        {
            return await workerTask;
        }
        catch (OperationCanceledException )
        {
            return 0;
        }
    }
于 2018-08-23T08:11:34.257 に答える