Task.Run和Task.Factory.StartNew之间的异常处理

时间:2013-02-06 13:00:44

标签: c# exception task-parallel-library taskfactory

我在使用Task.Factory.StartNew并尝试捕获被抛出的exception时遇到了问题。在我的应用程序中,我有一个长期运行的任务,我想封装在Task.Factory.StartNew(.., TaskCreationOptions.LongRunning);

但是,当我使用Task.Factory.StartNew时,不会捕获异常。然而,当我使用Task.Run时,它正如我所期望的那样,我认为它只是Task.Factory.StartNew上的一个包装器(根据例如this MSDN article)。

这里提供了一个工作示例,不同之处在于使用Task.Run时将异常写入控制台,但使用Factory.StartNew时却没有。

我的问题是:
如果我有一个LongRunning任务可以抛出异常,我应该如何在调用代码中处理它们?

private static void Main(string[] args)
{
    Task<bool> t = RunLongTask();
    t.Wait();
    Console.WriteLine(t.Result);
    Console.ReadKey();
}

private async static Task<bool> 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");
    });

}

3 个答案:

答案 0 :(得分:16)

您的问题是StartNewTask.Run代表的async不起作用。 StartNew的返回类型为Task<Task>(可转换为Task)。 “outer”Task表示方法的开头,“inner”Task表示方法的完成(包括任何例外)。

要转到内部Task,您可以使用Unwrap。或者,您可以使用Task.Run代替StartNew代码asyncLongRunning只是一个优化提示,实际上是可选的。 Stephen Toub has a good blog post on the difference between StartNew and Run and why Run is (usually) better for async code.

从下面的@usr评论更新: LongRunning仅适用于async方法的开头(直到第一个未完成的操作为await) 。所以在这种情况下使用Task.Run几乎肯定更好。

答案 1 :(得分:2)

我会将我的一些评论写入答案,因为他们证明是有用的:

LongRunning与强制在实践中创建新线程完全相同。并且你的异步方法可能很长一段时间都不在那个线程上(它在第一个等待点被取消)。在这种情况下你不需要LongRunning。

异步方法运行多长时间无关紧要。线程在第一次等待时被销毁(对未完成的任务进行操作)。

编译器能否以任何方式使用此提示?编译器通常无法以任何主要方式分析您的代码。此外,编译器对TPL一无所知。 TPL是一个图书馆。而这个库将始终启动一个新的线程。指定LongRunning如果您的任务几乎总是将100%CPU刻录多秒,或者以非常高的概率阻止多秒。

我的猜测你不想在这里LongRunning,因为如果你阻止了,为什么你首先使用异步? async是关于不阻止但是脱离线程。

答案 2 :(得分:0)

当你第一次Unwrap任务时,应该可以这样做:

await RunTaskAsync().Unwrap();

或者:

await await RunTaskAsync();