c# - 任务中的类似代码返回不同的状态代码

标签 c# .net asynchronous task

我从三个不同的任务中抛出一个 OperationCanceledException ,每个任务都有细微的差别,如下代码所示:

static async Task ThrowCancellationException()
{
    throw new OperationCanceledException();
}

static void Main(string[] args)
{
    var t1 = new Task(() => throw new OperationCanceledException());
    t1.Start();
    try { t1.Wait(); } catch { }

    Task t2 = new Task(async () => throw new OperationCanceledException());
    t2.Start();
    try { t2.Wait(); } catch { }

    Task t3 = ThrowCancellationException();

    Console.WriteLine(t1.Status); // prints Faulted
    Console.WriteLine(t2.Status); // prints RanToCompletion
    Console.WriteLine(t3.Status); // prints Canceled
}

我的问题是:

为什么每个任务的状态不同?

我可以理解,标记有 async 的代码/lambda 和未标记 async 的 lambda 之间存在差异,但即使在 之间,状态也不同async lambda 和运行相同代码的 async 方法。

最佳答案

I could understand that there are differences between the code/lambdas marked with async and the lambda not marked with async but the status is different even between the async lambda and the async method running the same code.

这并不完全正确。

如果你仔细观察 new Task(async () => throw new OperationCanceledException()) ,你会发现它正在调用重载 new Task(Action action) (没有重载需要 Func<Task> )。这意味着它相当于传递 async void 方法,而不是 async Task 方法。

<小时/>

所以:

Task t2 = new Task(async () => throw new OperationCanceledException());
t2.Start();
try { t2.Wait(); } catch { }

编译后的结果如下:

private static async void CompilerGeneratedMethod()
{
    throw new OperationCanceledException()
}
...
Task t2 = new Task(CompilerGeneratedMethod);
t2.Start();
try { t2.Wait(); } catch { }

这会从线程池中获取一个线程,并在其上运行 CompilerGeneratedMethod。当从 async void 方法内部抛出异常时,该异常会在适当的地方重新抛出(在本例中,它是在 ThreadPool 上重新抛出),但 CompilerGeneratedMethod 方法本身会立即返回。这会导致 Task t2 立即完成,这就是它的状态为 RanToCompletion 的原因。

那么异常是怎么回事呢?它即将导致您的申请失败!在 Console.ReadLine 的末尾粘贴 Main ,并在您有机会按 Enter 之前看到应用程序退出。

<小时/>

这个:

Task t3 = ThrowCancellationException();

非常不同。它不会尝试在线程池上运行任何内容。 ThrowCancellationException 同步运行,并同步返回包含 TaskOperationCanceledException 。包含 TaskOperationCanceledException 被视为 Canceled

<小时/>

如果您想在 ThreadPool 上运行 async 方法,请使用 Task.Run 。它有一个重载,需要 Func<Task> ,这意味着:

Task t2 = Task.Run(async () => throw new OperationCanceledException());

编译为如下内容:

private static async Task CompilerGeneratedMethod()
{
    throw new OperationCanceledException();
}
...
Task t2 = Task.Run(CompilerGeneratedMethod);

这里,当 CompilerGeneratedMethod 在 ThreadPool 上执行时,它返回包含 TaskOperationCanceledException 。然后,任务机制将 Task t2 转换为 Canceled 状态。

<小时/>

顺便说一句,请避免 new Task ,如果您想在 ThreadPool 上显式运行方法,则更喜欢使用 Task.Run 。 TPL中有很多方法是在async/await之前引入的,与它一起使用时会很困惑。

关于c# - 任务中的类似代码返回不同的状态代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60113345/

相关文章:

C#引用成员变量

c# - 为什么此 WPF 代码不生成上下文菜单?

c# - 退出时杀死进程

javascript - node.js 非阻塞 POST 请求等待另一个 POST 请求

ios - 在后台重新加载 UITableView 的数据

c# - CaSTLe Windsor 到 Unity - 你能像在 CW 中一样在 Unity 中自动配置吗?

c# - 在 WPF 的数据网格列中写入状态

c# - WPF:UI 元素未更新

c# - 推荐的 .NET 类用于唯一整数的集合?

c# - 如何将我的 WP7 界面绑定(bind)到我异步检索的数据?