【问题标题】:Why the Task doesn't show canceled status为什么任务不显示已取消状态
【发布时间】:2017-01-27 12:29:33
【问题描述】:

我正在测试一个在 Linqpad 上创建的简单控制台应用程序,其想法是确保了解 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. 我能够自信地找出 Completed 和 Faulted 状态,但即使在我看来这段代码会导致 Task 取消,IsCanceled 属性的值始终为 false。

    李> 1234563 >继续出错选项,但我假设,如果我能继续出错,它会变成假

【问题讨论】:

  • 取消是合作的。您的任务忽略了您的取消令牌,而是选择运行完成。因此,它没有被取消。
  • 我应该做哪些修改才能使其正常工作,我的其他理解是否正确,如果异常传播,那么它应该是故障未完成?

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


【解决方案1】:

我能够自信地找出 Completed 和 Faulted 状态,但即使在我看来这段代码应该导致任务取消,IsCanceled 属性的值也始终为 false。

取消没有自动性。您将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);
}

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

查看MSDN:

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

【讨论】:

  • 感谢您提供全面的详细信息并回答所有问题
【解决方案2】:

其他人注意到您的代码没有观察到CancellationToken,这就是任务没有被取消的原因。

我将回答这部分问题:

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

为此,请将您的逻辑放在await 任务之后:

await t;
// Your logic here.

使用IsCanceled / IsFaulted / IsCompleted 进行控制流是一种代码味道。

【讨论】:

  • 感谢@StephenCleary 提供重要的细节,事实上我的代码就是这样,我在List<Task> 中包含了许多不同的Task<T>,我已经调用了await Task.WhenAll(TaskList),但我没有意识到这会向调用者抛出底层任务中的任何异常,我认为这会默默地嵌入异常,我需要明确找出哪些任务包含有效结果以及哪些任务出错/取消,但正如你已经澄清的那样, 使用 await 解包时这是不正确的
【解决方案3】:

您没有将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);
}

【讨论】:

  • 感谢您的回答,感谢,它有帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多