OK、私の質問は本当に簡単です。このコードが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();
}
キャンセルは協調的であり、リスナーには強制されません。リスナーは、キャンセル要求への応答で正常に終了する方法を決定します。
Task.Run
メソッド内にCancellationToken
にアクセスしてキャンセルを実装するコードを記述しなかったため、キャンセルのリクエストを事実上無視して完了まで実行しました。
実行中のタスクと実行予定のタスクのキャンセルには違いがあります。
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
はOperationCanceledException
から派生しているため、catch
タイプに対してOperationCanceledException
句を1つだけ持つことができます。
catch (OperationCanceledException ex)
{
if (ex is TaskCanceledException)
// Task was canceled before running.
// Task was canceled while running.
}
CancellationTokenオブジェクトからメソッド ThrowIfCancellationRequested()
を呼び出していないためだと思います。このようにして、タスクのキャンセルのリクエストを無視しています。
次のようにする必要があります。
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
がすぐにスローされます。
代わりにToken.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;
}
}