任务的取消

6
我尝试运行一个简单的任务取消示例,类似于下面的示例。
CancellationTokenSource tokenSource2 = new CancellationTokenSource();

CancellationToken token2 = tokenSource2.Token;


Task task2 = new Task(() =>
{
    for (int i = 0; i < int.MaxValue; i++)
    {
        token2.ThrowIfCancellationRequested();
        Thread.Sleep(100);
        Console.WriteLine("Task 2 - Int value {0}", i);
    }
}, token2);

task2.Start();

Console.WriteLine("Press any key to cancel the task");
Console.ReadLine();

tokenSource2.Cancel();
Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled);

我原本期望 Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled); 会打印出 **"Task 2 cancelled? True"**,但它却打印出了"False"

你知道发生了什么吗?这是期望的行为吗?谢谢。

编辑:为确保在取消请求被调用之前任务未完成,我添加了Console.ReadLine()

3个回答

5

首先,也许你误解了IsCanceled的含义?它并不意味着“这个Task正在等待取消,因此它应该很快完成”,而是意味着“这个Task已经被取消了,现在它已经完成”。

如果你没有误解这一点,那么想想事件的确切顺序是什么。事件的发生如下:

  1. 调用ThrowIfCancellationRequested(),但是令牌还没有被取消,所以它不会抛出异常。
  2. 调用Thread.Sleep(),所以运行Task的线程睡眠。
  3. 调用Cancel()
  4. 检查 IsCanceledTask中的代码没有机会意识到令牌已被取消,所以它仍在运行,因此IsCanceled 返回false
  5. 再次调用ThrowIfCancellationRequested(),这一次它会抛出异常,实际上取消了Task

这就是为什么IsCanceled对你返回false。如果你希望它返回true,你可以在检查IsCanceled之前添加类似Thread.Sleep(150)的内容,或者更好的做法是等待Task完成。


2
任务在您调用取消之前已经结束,可以查看下面的内容,这可能有助于更好地解决您的问题。
从此处阅读http://social.msdn.microsoft.com/Forums/da-DK/parallelextensions/thread/9f88132a-f8bd-4885-ab63-645d7b6c2127,似乎令牌用于在任务“真正”启动之前但在任务排队后取消任务。

这更像是一种取消预定任务的方式,但尚未开始。一旦任务正在运行,取消它的唯一方法是通过方法中的协作检查。如果没有这个,你就必须始终启动任务,然后在内部进行检查,这会增加很多额外、不必要的开销。

您甚至可以从Cancellation token in Task constructor: why?中阅读到这篇文章。

2
这不是答案,但在评论中写太长了。
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = cancellationTokenSource.Token;

Task task = new Task(() =>
{
    for (int i = 0; i < int.MaxValue; i++)
    {
        cancellationToken.ThrowIfCancellationRequested();
        Console.WriteLine("Task 2 - Int value {0}", i);
    }
}, cancellationToken);

task.Start();

cancellationTokenSource.Cancel();
try
{
    task.Wait();
}
catch (AggregateException ae)
{
    if(ae.InnerExceptions.Single() is TaskCanceledException)
        Console.WriteLine("Caught TaskCanceledException");
    else
        Console.WriteLine("Did not catch canceled");
}
Console.WriteLine("Task 2 cancelled? {0}", task.IsCanceled);
  • 上述代码输出了预期的结果,我从未打印出“任务2 - 整数值{0}”,因此我认为它在取消之前没有完成。
  • 请注意,在我的示例中取消任务会抛出异常。
  • 有一些使用任务处理异常的模式可能值得一读。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接