嵌套异步/等待

时间:2019-08-21 06:36:01

标签: c# exception async-await console-application

我有一个.NET Core控制台应用程序。永远不会引发嵌套异步/等待中发生的异常:

    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),则会捕获到异常。

2 个答案:

答案 0 :(得分:5)

那是一个经典的陷阱。 TaskFactory期望Func<T>并返回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();

答案 1 :(得分:2)

您的错误来自StartNew。

当您等待StartNew时,您希望其中的作业能够完成。但是,您提供的工作是创建任务。您的StartNew具有以下返回类型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>,这使您的单次等待效率很高