Task.Run和Task.Factory.StartNew之间的exception处理不同
我在使用Task.Factory.StartNew
并尝试捕获抛出的exception
时遇到了一个问题。 在我的应用程序中,我有一个长期运行的任务,我想封装在Task.Factory.StartNew(.., TaskCreationOptions.LongRunning);
但是,当我使用Task.Factory.StartNew
时,不会捕获exception。 然而,当我使用Task.Run
,它正如我所期望的Task.Run
,我认为它只是Task.Factory.StartNew
一个包装器(根据例如这篇MSDN文章 )。
这里提供了一个工作示例,区别在于在使用Task.Run
时将exception写入控制台,但在使用Factory.StartNew
时则不会。
我的问题是:
如果我有一个可以抛出exception的LongRunning
任务,我应该如何在调用代码中处理它们?
private static void Main(string[] args) { Task t = RunLongTask(); t.Wait(); Console.WriteLine(t.Result); Console.ReadKey(); } private async static Task RunLongTask() { try { await RunTaskAsync(); } catch (Exception e) { Console.WriteLine(e); return false; } Console.WriteLine("success"); return true; } private static Task RunTaskAsync() { //return Task.Run(async () => // { // throw new Exception("my exception"); // }); return Task.Factory.StartNew( async () => { throw new Exception("my exception"); }); }
您的问题是StartNew
不像Task.Run
使用async
委托。 StartNew
的返回类型是Task
(可以转换为Task
)。 “外部” Task
表示方法的开头,“内部” Task
表示方法的完成(包括任何例外)。
要进入内部Task
,您可以使用Unwrap
。 或者您可以使用Task.Run
而不是StartNew
来获取async
代码。 LongRunning
只是一个优化提示,实际上是可选的。 Stephen Toub有一篇关于StartNew
和Run
之间差异的博文,以及为什么Run
(通常)更适合async
代码。
从下面的@usr注释更新: LongRunning
仅适用于async
方法的开头(直到第一个未完成的操作await
编辑)。 所以在这种情况下使用Task.Run
几乎肯定更好。
我会将一些评论写入答案中,因为它们对我们有所帮助:
LongRunning
与强制在实践中创建新线程完全相同。 并且你的异步方法可能很长时间都不在该线程上(它在第一个等待点处被取消)。 在这种情况下,您不希望LongRunning。
async方法运行多长时间并不重要。 线程在第一次等待时被销毁(对未完成的任务进行操作)。
编译器可以以任何方式使用此提示吗? 编译器通常无法以任何主要方式分析您的代码。 此外,编译器对TPL一无所知。 TPL是一个图书馆。 而这个库将始终启动一个新的线程。 指定LongRunning
如果您的任务几乎总是将100%CPU刻录多秒,或者以非常高的概率阻止多秒。
我的猜测是你不想在这里使用LongRunning
,因为如果你阻塞了,为什么你首先使用异步? async是关于不阻止但是脱离线程。
首次打开任务时应该可以:
await RunTaskAsync().Unwrap();
或者:
await await RunTaskAsync();