嵌套 async/await 未抛出异常

Exception not thrown from nested async/await

我有一个 .NET Core 控制台应用程序。永远不会抛出嵌套 async/await 中发生的异常:

    static async Task Main(string[] args)
    {
        try
        {
            var f = new TaskFactory(TaskScheduler.Current);

            await f.StartNew(async () =>
            {
                var x = 0;
                if (x == 0)
                    throw new Exception("we have a problem");

                await Task.Delay(1);
            });
        }
        catch(Exception)
        {
            // never reaches here
        }
    }

如果我删除内部 async,并放弃对 await Task.Delay(1) 的调用,则会捕获异常。

这是一个经典的陷阱。 TaskFactory 期望 Func<T> 和 returns 一个 Task<T>。在你的例子中,TTask,因此你最终得到一个 Task<Task> 并且你需要等待内部和外部任务。为此使用 Unwrap

await f.StartNew(async () =>
{
    var x = 0;
    if (x == 0)
        throw new Exception("we have a problem");

    await Task.Delay(1);
}).Unwrap();

您的错误来自StartNew。

当您等待 StartNew 时,您希望里面的作业完成。然而,你给它的工作是创建一个任务。您的 StartNew 具有以下 return 类型 Task<Task>。 因为它是您的代码在抛出异常之前结束。

您可以通过两种不同的方式解决:

1) 等待第一次等待完成后获得的新开始结果

    static async Task Main(string[] args)
    {
        try
        {
            var f = new TaskFactory(TaskScheduler.Current);

            await await f.StartNew(async () =>
            {
                var x = 0;
                if (x == 0)
                    throw new Exception("we have a problem");

                await Task.Delay(1);
            });
        }
        catch(Exception)
        {
            Console.WriteLine("Exception received");
            // never reaches here
        }
        Console.WriteLine("Done");
    }

2) 您停止使用 StartNew 并使用基本任务模式(我推荐)

    static async Task Main(string[] args)
    {
        try
        {
            await Task.Run(async () =>
            {
                var x = 0;
                if (x == 0)
                    throw new Exception("we have a problem");

                await Task.Delay(1);
            });
        }
        catch(Exception)
        {
            Console.WriteLine("Exception received");
            // never reaches here
        }
        Console.WriteLine("Done");
    }

之所以删除 Task.Delay(1) 解决了您的问题,是因为您将 StartNew 的签名更改为 Task 而不是 Task<Task>,这使您的单次等待变得高效