【问题标题】:Not awaitable method exception thowing inconsistent behaviour不可等待的方法异常导致不一致的行为
【发布时间】:2020-05-10 06:20:00
【问题描述】:

所以我在 async/await 和某个地方读到过,如果你不等待异步方法,你基本上会丢失它。它会触发并忘记并进入 AEeher,如果它抛出异常 - 你永远不会知道。
这是作者使用的示例:

    static async void OnButtonClick()
    {
        yolo();
        string imageUrl = null;
        try
        {
            DownloadAndBlur(imageUrl);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception: {ex}");
        }
        Console.WriteLine("Done!");
    }

    static async Task DownloadAndBlur(string url)
    {
        if (url == null)
        {
            throw new ArgumentNullException(nameof(url));
        }
    }

确实,如果我从我的代码中调用 OnButtonClick() 方法,则不会引发异常,或者更确切地说,控制台上不会打印任何异常信息。而如果我等待 DownloadAndBlur 方法 - 一个异常被写入控制台。

所以我试图复制这种行为并写了这个:

    static async void Execute()
    {
        Console.WriteLine(1);
        yolo();
        Console.WriteLine(2);
    }

    static Task yolo()
    {
        throw new Exception();
    }

但是抛出了一个异常,我的调试会话捕获了它。那么有什么不同,因为我认为它们是相同的。

【问题讨论】:

  • static Task yolo() 不是异步方法,这是一个返回 Task 类型值的方法。

标签: c# asynchronous async-await


【解决方案1】:

Execute 方法不是一劳永逸的。是async void

static async void Execute()
{
    YoloAsync();
}

async void 方法中的异常会在当前SynchronizationContext 中抛出(如果SynchronizationContext.Current 为空,则在ThreadPool 中),这通常会导致进程崩溃(source code)。

接下来,YoloAsync 方法没有用async 修饰符标记。

static Task YoloAsync()
{
    throw new Exception();
}

Semantically 它是一个异步方法,因为它返回一个Task,但任务不是从异步状态机生成的。因此,YoloAsync 方法中的代码与任何其他方法一样同步执行,与通过返回的Task 传播异常的异步状态机生成的方法不同。

【讨论】:

  • 啊,该死的,我被错误地误导为相信async 只是允许您在方法中使用await 关键字。谢谢!
  • @user13288253 是的,这也是我的想法。显然,总有一些意想不到的错综复杂的事情潜伏在我们身边,让我们的生活困难变得有趣。 :-) 如果你想看看 this Stephen Cleary 的相关博文。
  • “如果 Current 为 null,则默认 SynchronizationContext”是什么意思?
  • @PauloMorgado 这是一个错误。我的印象是异常是在基类SynchronizationContext 的缓存实例上引发的,但它实际上是直接在ThreadPool 上引发的。我编辑了答案以更正此错误。
【解决方案2】:

格言 26。“一劳永逸”很好,前提是您永远不会真正忘记。

在多任务处理中获得异常总是很困难的。

如果你在做多线程,默认情况下释放所有异常实际上很容易。线程本身会吞下所有异常。这是必须处理的最坏情况。

因此,多任务方法总是捕获所有异常,然后在结果中公开它们。 Task has a property for thatRunWorkerCompletedEventArgs也是如此。

延续代码的一项工作是检查并重新引发任何异常。这是 Task 必须为多线程支持做的事情,即使它对仅仅多任务处理没有意义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-20
    • 1970-01-01
    • 2020-09-02
    相关资源
    最近更新 更多