在Task.Run中使用CancellationToken超时不起作用

好的,我的问题非常简单。 为什么这段代码不会抛出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并实现取消 – 因此您实际上忽略了取消请求并运行完成。

我想因为你没有从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(); } 

代码的第二个变体可以工作,因为您已经在Canceled状态设置为true的情况下初始化了一个令牌。 的确,如上所述:

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

已经请求取消,然后将立即抛出exceptionTaskCanceledException ,而不实际启动任务。

取消正在运行的任务和计划运行的任务有所不同。

在调用Task.Run方法之后,该任务仅被调度,并且可能尚未执行。

当您使用具有取消支持的Task.Run(…,CancellationToken)重载系列时,将在任务即将运行时检查取消令牌。 如果取消令牌此时将IsCancellationRequested设置为true,则抛出TaskCanceledException类型的exception。

如果任务已在运行,则任务负责调用ThrowIfCancellationRequested方法,或者只是抛出OperationCanceledException。

根据MSDN,它只是一种方便的方法:

if(token.IsCancellationRequested)抛出新的OperationCanceledException(token);

不是在这两种情况下使用的不同类型的exception:

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

另请注意, TaskCanceledException派生自OperationCanceledException ,因此您只能为OperationCanceledException类型设置一个catch子句:

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

使用带有令牌的Task.Delay的另一个实现,而不是Thread.Sleep。

  static void Main(string[] args) { var task = GetValueWithTimeout(1000); Console.WriteLine(task.Result); Console.ReadLine(); } static async Task 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; } }