为什么会发生TaskCanceledException?

时间:2013-03-03 02:36:53

标签: c# task-parallel-library c#-5.0

我有以下测试代码:

void Button_Click(object sender, RoutedEventArgs e)
{
    var source = new CancellationTokenSource();

    var tsk1 = new Task(() => Thread1(source.Token), source.Token);
    var tsk2 = new Task(() => Thread2(source.Token), source.Token);

    tsk1.Start();
    tsk2.Start();

    source.Cancel();

    try
    {
        Task.WaitAll(new[] {tsk1, tsk2});
    }
    catch (Exception ex)
    {
        // here exception is caught
    }
}

void Thread1(CancellationToken token)
{
    Thread.Sleep(2000);

    // If the following line is enabled, the result is the same.
    // token.ThrowIfCancellationRequested();
}

void Thread2(CancellationToken token)
{
    Thread.Sleep(3000);
}

在线程方法中,我没有抛出任何异常,但是我在外部代码的TaskCanceledException块中得到了try-catch,它启动了任务。为什么会发生这种情况,在这种情况下token.ThrowIfCancellationRequested();的目的是什么。我相信只有在线程方法中调用token.ThrowIfCancellationRequested();时才会抛出异常。

1 个答案:

答案 0 :(得分:24)

我认为这是预期的行为,因为你正在参与竞争条件的变化。

来自How to: Cancel a task and its children

  

调用线程不强制结束任务;它只表示要求取消。如果任务已在运行,则由用户委托决定通知请求并做出相应的响应。如果在任务运行之前请求取消,则永远不会执行用户委托,并且任务对象将转换为Canceled状态。

Task Cancellation

  

您可以通过简单地从代表返回来终止操作。在许多情况下,这已足够;但是,以这种方式“取消”的任务实例将转换为RanToCompletion状态,而不是Canceled状态。

我在这里受过教育的猜测是,当你在呼唤你的两个任务.Start(),有机会,一个(或两者)并没有真正开始你叫之前.Cancel()您{{ 1}}。我打赌如果你在任务开始和取消之间至少等待三秒钟,它就不会抛出异常。此外,您可以检查两个任务的CancellationTokenSource属性。如果我是对的,那么当抛出异常时,.Status属性应至少读取其中一个TaskStatus.Canceled

请记住,启动新的.Status并不能保证创建新的线程。它由TPL决定什么才能获得一个新线程以及什么只是排队等待执行。