【问题标题】:Why is the exception not being caught?为什么异常没有被捕获?
【发布时间】:2017-06-18 04:34:35
【问题描述】:

我认为我的理解中缺少一些东西。如果我在 Console.WriteLine 完成的地方设置断点,它不会停止。

private static void Main(string[] args)
{
    Process(async () => await ClientMethod()).Invoke();
    Console.Read();
}

public static async Task ClientMethod()
{
    throw new Exception("Test");
}

public static Action Process(Action functor)
{
    return () =>
    {
        try
        {
            functor();
        }
        catch (Exception)
        {
            // Handle exceptions ?
            Console.WriteLine("In the catch");
            throw;
        }
    };
}

但是如果我将我的代码更改为这样,通过删除异步行为,就会触发断点:

private static void Main(string[] args)
{
    Process(() => ClientMethod()).Invoke();
    Console.Read();
}

public static void ClientMethod()
{
    throw new Exception("Test");
}

为什么在第一种情况下没有捕获到异常?我怎样才能抓住它?

编辑:我把我的代码改成了这个,但还是一样:

private static void Main(string[] args)
{
    var res = Process(async () => await ClientMethod()).Invoke();
    Console.Read();
}

public static async Task<string> ClientMethod()
{
    throw new Exception("Test");
}

public static Func<T> Process<T>(Func<T> functor)
{
    return () =>
    {
        try
        {
            return functor();
        }
        catch (Exception)
        {
            // Handle exceptions ?
            Console.WriteLine("In the catch");
            throw;
        }
    };
}

【问题讨论】:

    标签: c# .net exception-handling async-await


    【解决方案1】:

    因为 Process 中的操作被作为 async void 调用。

    引用:来自Async/Await - Best Practices in Asynchronous Programming

    Async void 方法具有不同的错误处理语义。当一个 async Taskasync Task&lt;T&gt; 方法抛出异常,即 异常被捕获并放置在 Task 对象上。使用异步无效 方法,没有 Task 对象,所以任何异常都会抛出 async void 方法将直接在 SynchronizationContext 在异步 void 方法时处于活动状态 开始了。

    【讨论】:

    • 那么,如果我有一个带有返回值而不是 Action 的 Func,它应该可以工作吗?
    • 我已经用 Func 而不是 Action 编辑了我的问题,所以它不再是 async void 方法,但仍然没有捕获到异常
    • 也可以看看这篇同作者的短文blog.stephencleary.com/2012/02/async-console-programs.html
    • 一个简短的(并且有些被截断的)概要是任何异步任务都在单独的线程上运行并且需要处理它自己的异常。任何未处理的异常都不会冒泡到主线程异常处理程序。这种性质的未处理异常可能会导致应用程序不稳定和奇怪的行为。
    • @Theo async 方法实际上不一定在另一个线程中运行。他们可能在另一个线程中运行,他们可能在单个调用线程中完成所有工作,或者他们可能根本不使用线程来完成他们的工作(或所有这些的某种组合)。最后一部分也是错误的。正如这个答案所指出的,async 方法中的异常被捕获,并在各种情况下被重新抛出。对于void 方法,它被推送到同步上下文,这可能是主线程的消息循环,所以它直接进入主线程的异常处理程序正是正在发生的事情。
    【解决方案2】:

    您的第一个代码的问题是 Action 导致创建 async void 方法。您应该 avoid async void 有几个原因 - 其中之一是异常处理很奇怪(异常不会以任何合理的方式从 async void 方法传播)。

    您的第二个代码很奇怪。如果您记得TTask&lt;string&gt;,您可以更清楚地看到发生了什么。因此,您的包装委托将返回一个Task&lt;string&gt;;它不会观察到它的异常。 To observe exceptions from a task-returning method, you should await the task it returns,而您的 try 块中的代码不是 await 执行任务 - 它只是直接返回它。

    解决方案是回到第一个示例。您要做的是将Action 委托类型更改为其异步等效项which is Func&lt;Task&gt;。从这里开始,解决方案更自然地流动:

    public static Func<Task> Process(Func<Task> functor)
    {
      return async () =>
      {
        try
        {
          await functor();
        }
        catch (Exception)
        {
          Console.WriteLine("In the catch");
          throw;
        }
      };
    }
    

    用法:

    await Process(async () => await ClientMethod()).Invoke();
    
    public static async Task ClientMethod()
    {
      throw new Exception("Test");
    }
    

    【讨论】:

    • 此时也没有理由让 lambda 成为async,甚至没有理由使用 lambda;您可以将 ClientMethod 作为方法组传入,或者如果您真的想要一个 lambda,那么非异步 lambda 就可以了。
    • @Servy:是的,但是鉴于op的理解水平,我根本没有清理使用代码。
    猜你喜欢
    • 1970-01-01
    • 2010-11-16
    • 2012-01-22
    • 1970-01-01
    • 1970-01-01
    • 2020-11-16
    相关资源
    最近更新 更多