CancellationTokenSource的行为不符合预期
本文关键字:不符合 CancellationTokenSource | 更新日期: 2023-09-27 18:09:36
在这种情况下所期望的是,如果用户通过按enter键取消任务,则ContinueWith
所钩子的另一个任务将运行,但事实并非如此,尽管ContinueWith
中显式处理显然没有被执行,但AggregateException
仍然被抛出。
请澄清一下以下内容?
class Program
{
static void Main(string[] args)
{
CancellationTokenSource tokensource = new CancellationTokenSource();
CancellationToken token = tokensource.Token;
Task task = Task.Run(() =>
{
while (!token.IsCancellationRequested)
{
Console.Write("*");
Thread.Sleep(1000);
}
}, token).ContinueWith((t) =>
{
t.Exception.Handle((e) => true);
Console.WriteLine("You have canceled the task");
}, TaskContinuationOptions.OnlyOnCanceled);
Console.WriteLine("Press any key to cancel");
Console.ReadLine();
tokensource.Cancel();
task.Wait();
}
}
让我们从一些事实开始:
- 当你传递一个
CancellationToken
作为Task.Run
的参数时,它只有在任务开始运行之前取消才有效果。如果任务已经在运行,它将而不是被取消。 - 在开始运行后,要取消任务,您需要使用
CancellationToken.ThrowIfCancellationRequested
,而不是CancellationToken.IsCancellationRequested
。- 如果一个任务被取消,它的
Exception
属性不包含任何异常,并且是null
。- 如果延续任务由于某种原因没有运行,这意味着它被取消了。
- 一个任务包含来自它自己和它的所有子任务的异常(因此,
AggregateException
)。 - 如果一个任务被取消,它的
这就是在你的代码中发生的事情:
任务开始运行,因为令牌没有被取消。它将一直运行,直到令牌被取消。在它结束之后,continuation 将不会运行,因为它只在前一个任务被取消时运行,并且它还没有被取消。当您Wait
任务时,它将抛出AggregateException
和TaskCanceledException
,因为延续被取消(如果您将删除该延续,异常将消失)。
解决方案:
您需要修复任务,以便它实际上被取消,并删除(或null检查)异常处理,因为没有异常:
var task = Task.Run(new Action(() =>
{
while (true)
{
token.ThrowIfCancellationRequested();
Console.Write("*");
Thread.Sleep(1000);
}
}), token).ContinueWith(
t => Console.WriteLine("You have canceled the task"),
TaskContinuationOptions.OnlyOnCanceled);
如果将令牌作为第二个参数传递,则任务将无法顺利继续,因为它实际上已经被取消了。相反,它抛出OperationCanceledException,该异常被包装在AggregateException中。这完全在意料之中。现在,如果你没有将令牌传递给任务构造函数,那么你会看到你所期望的行为,因为因为你只会使用令牌作为退出while循环的标志。在这种情况下,您并没有真正取消任务,而是退出while循环并正常完成任务。