从Task.Run抛出异常会显示"Not Notled from User Code"消息

Jes*_*ood 7 .net c# task-parallel-library async-await

代码示例:

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Start");
        Foo f = new Foo();
        f.FooAsync();
    }

    public class Foo
    {
        public async void FooAsync()
        {
            try
            {
                await Task.Run(() =>
                {
                    Console.WriteLine("Throwing");
                    throw new Exception();
                });
            }
            catch (Exception)
            {
                Console.WriteLine("Caught");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码从C#小提琴或从我的PC上的控制台运行时打印:

Start
Throwing
Caught
Run Code Online (Sandbox Code Playgroud)

C#小提琴示例

但是,当我从Visual Studio(调试模式下的F5)运行它时,我收到以下消息:

例外图片

我不明白为什么我从Visual Studio得到"没有在用户代码中处理"消息,尽管从控制台或C#小提琴运行它是好的.我错过了一些明显的东西吗

UPDATE

我已经尝试f.FooAsync().Wait();在Main中等待任务,但仍然报告异常未处理(相同的错误消息).

i3a*_*non 10

首先,这个异常的特殊之处在于它被抛出Task.Run并且对于该范围,异常实际上不是由用户代码处理的.

框架捕获并将异常存储在返回的任务中供您稍后处理,如果您没有这样做,那么这是一个未被观察到的任务异常(在.Net 4.0中会使应用程序崩溃).

这里的问题是设置中的"启用我的代码"功能.当打开该功能时,调试器会在您离开用户代码并输入框架代码时中断异常,此时异常确实未得到处理.

如果您关闭该功能,调试器将不会中断该异常,因为它将跟随您的代码进入框架并查看正在处理并存储在任务中的异常.然后,当您await执行任务并由代码处理时,将重新抛出异常.


由于实际上重要的处理是由框架完成的处理,这是一个更简单的例子:

public static void Main()
{
    Task.Run(() =>
    {
        throw new Exception();
    });
    Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)

启用"启用刚才我的代码"后,调试器将中断异常,如果没有调试器则不会.