13

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

但是,当我使用Task.Factory.StartNew. 然而,当我使用它时Task.Run,它可以正常工作,我认为它只是一个包装器Task.Factory.StartNew(例如根据这篇 MSDN 文章)。

此处提供了一个工作示例,不同之处在于异常在使用时写入控制台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");
    });

}
4

3 回答 3

16

您的问题是StartNew与代表不同。的返回类型是(可转换为)。“outer”表示方法的开始,“inner”表示方法的完成(包括任何异常)。Task.RunasyncStartNewTask<Task>TaskTaskTask

要进入内部Task,您可以使用Unwrap. 或者您可以只使用而Task.Run不是代码。只是一个优化提示,实际上是可选的。Stephen Toub 有一篇很好的博客文章,介绍了代码之间的区别以及为什么(通常)更好StartNewasyncLongRunningStartNewRunRunasync

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

于 2013-02-06T13:07:09.233 回答
2

我会将我的一些评论纳入答案,因为事实证明它们很有帮助:

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

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

编译器可以以任何方式使用此提示吗?编译器通常无法以任何主要方式分析您的代码。此外,编译器对 TPL 一无所知。TPL 是一个库。这个库总是会启动一个新线程。指定LongRunning当你的任务几乎总是在几秒钟内消耗 100% CPU 或以非常高的概率阻塞几秒钟。

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

于 2013-02-06T14:02:09.883 回答
0

首次展开任务时应该可以:

await RunTaskAsync().Unwrap();

或者:

await await RunTaskAsync();
于 2013-02-06T13:29:17.403 回答