在异步操作的任务中在哪里处理异常?

Where to handle an exception in a task of async action?

示例代码是这样的:

Action action = async () =>
{
    Console.WriteLine("Action start...");
    await Task.Delay(1000);
    throw new Exception("Exception from an async action");
};

Task.Run(action);

Console.ReadKey(); 

在哪里处理异常?

由于您使用 Task.Run(action); 而您没有 等待 返回的任务对象,异常在另一个线程中抛出,您无法在调用者线程(例如使用 ContinueWith);

然后您需要在 Action 委托中处理异常:

Action action = async () =>
{
    try
    {
        Console.WriteLine("Action start...");
        await Task.Delay(1000);
        throw new Exception("Exception from an async action");
    }
    catch(Exception ex)
    {
        // do something
    }
};

您可以在任务本身内部或调用者外部处理它,只需注意 Task.Run 上的等待,这可确保您捕获异常而不是使其无声死亡。`

Func<Task> action = async () =>
{
    Console.WriteLine("Action start...");
    await Task.Delay(1000);
    throw new Exception("Exception from an async action");
};

try
{
   await Task.Run(action);
}
catch(Exception ex)
{
    Console.WriteLine(ex.Message);
}

Console.ReadKey(); 

此外,检查此 post 关于 async/await 异常处理的差异

人们常常认为使用 async-await 时会涉及多个线程,但实际上并非如此,除非您指定这样做。

阅读 Eric Lippert about async-await 在中间某处搜索异步等待。

他把 async await 比作一个厨师:在烤面包时,他可以等到面包烤好,然后再开水泡茶和鸡蛋。如果他开始烧水,然后回头看面包是否烤好,效率会更高。

您的代码中也会发生同样的情况。在 await task.Delay 期间,您的线程不会开始等待。取而代之的是,它会在其调用堆栈中上升,以查看其中一个调用者(他们都必须是异步的!)是否没有在等待,因此可以在没有调用结果的情况下继续处理。过了一会儿,它 returns 查看 Task.Delay 是否完成并继续下一个抛出异常的语句。

请注意,在这种情况下,只涉及一个线程。异常捕获与所有其他异常捕获一样完成。虽然捕手可以检查调用堆栈,但他不确定哪些代码段被执行了,哪些没有。在这方面与非异步等待没有太大区别

代码有两处错误。

首先,它使用了一个 async void 委托,它可以防止异常正常工作(有关避免 async void 的更多信息,请参阅 my MSDN article on async best practices). It should be using Func<Task> instead of Action (for more information on async-friendly delegate types, see my blog post on synchronous and asynchronous delegate types):

Func<Task> action = async () =>
{
  Console.WriteLine("Action start...");
  await Task.Delay(1000);
  throw new Exception("Exception from an async action");
};

第二个错误是当 运行 线程池上的委托时它使用了即发即弃。 "fire-and-forget" 的 "forget" 部分表示 "ignore all exceptions"。要正确传播异常,应等待从 Task.Run 返回的任务(有关 await 如何处理任务的更多信息,请参阅我的 blog post on async and await):

await Task.Run(action);