为什么任务没有显示已取消的状态

时间:2017-01-27 12:29:34

标签: c# async-await task-parallel-library task

我正在测试在Linqpad上创建的一个简单的控制台应用程序,想法是确保Task的工作有所了解并创建一个有效的逻辑,当Task 完成,出现故障或取消即可。我想只在Task完成但没有出现故障或取消时才执行逻辑。

void Main()
{
    CancellationTokenSource cts = new CancellationTokenSource(new TimeSpan(0,0,0,0,1000));

    Task t = Task.Run(() => Work(),cts.Token);
    try
    {
        t.Wait();
    }
    catch
    {
    }

    ("Completed :: " + t.IsCompleted).Dump();
    ("Canceled :: " + t.IsCanceled).Dump();
    ("Faulted :: " + t.IsFaulted).Dump();
}

public async Task Work()
{
    await Task.Delay(3000);
}

以下是问题:

  1. 我能够自信地找出已完成状态和故障状态,但即使在我看来此代码应该导致任务取消,IsCanceled属性的值始终为false。

  2. 理想情况下,当Task出现故障时,即使我在try catch块中静默捕获异常,它也应该将IsCompleted显示为false,但它始终保持为真,目前Linqpad并没有这样做。我有继续错误选项,但我假设,如果我可以继续出错,它会变为假

3 个答案:

答案 0 :(得分:5)

  

我能够自信地找出已完成状态和故障状态,但即使在我看来这个代码应该导致任务取消,IsCanceled属性的值总是为假。

取消时没有自动化。您正在将CancellationToken传递给Task.Run。如果在任务启动时取消,则取消将中断启动过程。任务运行后,任务的方法是检查取消令牌的责任。 Wait没有这样做。它甚至不知道取消令牌。因此,任务永远不会变成被取消的状态。

这是您观察取消的方式:

void Main()
{
    CancellationTokenSource cts = new CancellationTokenSource(new TimeSpan(0,0,0,0,1000));

    Task t = Task.Run(() => Work(cts.Token),cts.Token);
    try
    {
        t.Wait();
    }
    catch
    {
    }

    ("Completed :: " + t.IsCompleted).Dump();
    ("Canceled :: " + t.IsCanceled).Dump();
    ("Faulted :: " + t.IsFaulted).Dump();
}

public async Task Work(CancellationToken token)
{
    await Task.Delay(3000, token);
}
  

理想情况下,当Task出现故障时,即使我在try catch块中静默捕获异常,它也应该将IsCompleted显示为false,但它始终保持为真

检查MSDN

  

当任务处于以下三种最终状态之一时,IsCompleted将返回true:RanToCompletion,Faulted或Cancelled。

答案 1 :(得分:5)

其他人已经注意到您的代码没有遵守CancellationToken,这就是为什么任务没有被取消。

我会回答这部分问题:

  

我想只在任务完成但没有出现故障或取消时才执行逻辑。

为此,请在await任务之后输入逻辑:

await t;
// Your logic here.

使用IsCanceled / IsFaulted / IsCompleted来控制流量是代码气味。

答案 2 :(得分:2)

您没有将CancellationToken传递给Task.Delay方法,因此无需取消任何内容。 您在Task.Run(xxx)中传递的令牌可以防止在令牌未完成取消时启动工作。但是,您的令牌会在1秒钟后被取消,这是在致电Task.Run后很长时间。

试试这个:

void Main()
{
    CancellationTokenSource cts = new CancellationTokenSource(new TimeSpan(0, 0, 0, 0, 1000));

    Task t = Task.Run(() => Work(cts.Token), cts.Token);
    try
    {
        t.Wait();
    }
    catch
    {
    }

    ("Completed :: " + t.IsCompleted).Dump();
    ("Canceled :: " + t.IsCanceled).Dump();
    ("Faulted :: " + t.IsFaulted).Dump();
}

public async Task Work(CancellationToken t)
{
    await Task.Delay(3000, t);
}