为什么我不能从异步代码中捕获异常?

Dro*_*iss 26 .net c# c#-5.0

我读到它的任何地方都说下面的代码应该可行,但事实并非如此.

public async Task DoSomething(int x)
{
   try
   {
      // Asynchronous implementation.
      await Task.Run(() => {
      throw new Exception();
      x++;
      });
   }
   catch (Exception ex)
   {
      // Handle exceptions ?
   }
}
Run Code Online (Sandbox Code Playgroud)

也就是说,我没有抓到任何东西,并且在"投掷"线上得到一个"未处理的例外".我在这里很无能为力.

Mat*_*ith 31

您已打开"仅我的代码"选项.有了这个,它正在考虑关于"只是你的代码"未处理的异常 - 因为其他代码正在捕获异常并将其填充到Task中,稍后在await调用时重新抛出并由catch语句捕获.

如果没有附加在调试器中,您的catch语句将被触发,并且它将按预期运行.或者您可以从调试器中继续,它将按预期运行.

最好的办法是关闭"只是我的代码".国际海事组织,它导致更多的混乱而不是它的价值.

  • 嘿,一个真正的答案(在一群人中"它对我有用",好像他们认为这有助于任何人).肯定在VS中有很多选项导致巨大的,令人困惑的问题("启用本机代码调试"是另一个). (2认同)

ken*_*n2k 10

正如SLaks所说,你的代码运行良好.

我强烈怀疑你过度简化了你的例子,并async void在你的代码中有一个.

以下工作正常:

private static void Main(string[] args)
{
    CallAsync();
    Console.Read();
}

public static async void CallAsync()
{
    try
    {
        await DoSomething();
    }
    catch (Exception)
    {
        // Handle exceptions ?
        Console.WriteLine("In the catch");
    }
}

public static Task DoSomething()
{
    return Task.Run(() =>
    {
        throw new Exception();
    });
}
Run Code Online (Sandbox Code Playgroud)

以下不起作用:

private static void Main(string[] args)
{
    CallAsync();
    Console.Read();
}

public static void CallAsync()
{
    try
    {
        DoSomething();
    }
    catch (Exception)
    {
        // Handle exceptions ?
        Console.WriteLine("In the catch");
    }
}

public static async void DoSomething()
{
    await Task.Run(() =>
    {
        throw new Exception();
    });
}
Run Code Online (Sandbox Code Playgroud)

请参阅http://msdn.microsoft.com/en-us/magazine/jj991977.aspx

异步void方法具有不同的错误处理语义.当异步任务或异步任务方法抛出异常时,将捕获该异常并将其放在Task对象上.使用async void方法时,没有Task对象,因此异步void方法抛出的任何异常都将直接在async void方法启动时处于活动状态的SynchronizationContext上引发.图2说明了异步void方法抛出的异常无法自然捕获.


Jon*_*eet 8

由于x++;语句无法访问,您的代码目前甚至不会干净地编译.始终注意警告.

但是,在修复之后,它工作正常:

using System;
using System.Threading.Tasks;

class Test
{
    static void Main(string[] args)
    {
        DoSomething(10).Wait();
    }

    public static async Task DoSomething(int x)
    {
        try
        {
            // Asynchronous implementation.
            await Task.Run(() => {
                throw new Exception("Bang!");
            });
        }
        catch (Exception ex)
        {
            Console.WriteLine("I caught an exception! {0}", ex.Message);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

I caught an exception! Bang!
Run Code Online (Sandbox Code Playgroud)

(注意,如果你在WinForms应用程序中尝试上面的代码,你将会遇到死锁,因为你正在等待一个需要回到UI线程的任务.我们可以在控制台应用程序中作为任务将在线程池线程上恢复.)

我怀疑这个问题实际上只是一个调试问题 - 调试器可能会认为它未处理,即使它已被处理.