【问题标题】:One line fire and forget: void vs. async void + await一行触发并忘记:void vs. async void + await
【发布时间】:2023-03-23 05:32:01
【问题描述】:

我有一个void 事件处理程序,它只包含一行调用awaitable 方法的行。

void Handler( object sender, EventArgs args ) => AwaitableMethod();

将返回类型更改为Task 不是一种选择,因此无论语法糖如何,它都是一劳永逸的。即便如此,Visual Studio 在 unawaited 调用下放置了一个绿色波浪线,建议我调用处理程序 async voidawait

async void Handler( object sender, EventArgs args ) => await AwaitableMethod();

我的理解是,在这种情况下添加 async voidawait 只是无用的开销。 Visual Studio 是否知道一些我不知道的事情,还是只是不必要地烦人?

【问题讨论】:

  • 如果从AwaitableMethod 返回的Task 最终包含异常,则不同。第一个案例会默默吞下它;第二个将重新抛出它。您最好使用第二个选项,并意识到发生了异常。
  • @canton7 感谢您的提示!我必须重新回忆一下异常如何与async-await 一起工作。

标签: c# asynchronous async-await task task-parallel-library


【解决方案1】:

我的理解是,在这种情况下添加 async void 和 await 只是无用的开销。

没有。

如果Handler 返回了Task,那么这是正确的,eliding async/await 就可以了;没有async/await 的代码将直接返回Task,而不是用await“解包”它,然后用async“包”回Task

但是,这里不是这样; Handler 返回void,因此没有async/await 的代码将忽略返回的Task,这在绝大多数情况下都是错误的(因此会出现编译器警告)。具体来说,忽略Task 将忽略来自Task 的任何异常。您的代码也无法知道被忽略的 Task 何时完成,但大概这是可以接受的,因为您的处理程序正在返回 void

async void 方法会进行“注册”,以便框架知道有一个任务仍在进行中,因此框架知道何时可以安全关闭。唯一真正关心这一点的 .NET 提供的框架是 ASP.NET pre-Core;所有其他 .NET 提供的框架(包括所有 UI 框架)都会忽略该“注册”。

【讨论】:

  • 有没有办法捕获async void 事件处理程序中引发的异常?我意识到你可以在事件处理程序 within 中有一个 try-catch,但我想看看像 VS 建议的那样简单地在此处添加 await 从异常处理的角度来看会有所帮助。
  • @JLRishe 你可以订阅AppDomain.UnhandledException 事件。
  • @JLRishe:没有await,异常被默默吞噬;这几乎不是我们想要的行为。使用await,任何异常都会直接传递给SynchronizationContext。大多数框架都有一个顶级异常处理程序,您可以在其中做出响应,例如Application.DispatcherUnhandledExceptionApplication.ThreadException
  • @StephenCleary 我明白了。谢谢你的回复!
  • @StephenCleary 感谢您的出色回答。你说,“绝大多数时候都错了”。你能详细说明什么时候不会出错吗?
【解决方案2】:

在这种特殊情况下,它主要取决于您的错误处理。

如果AwaitableMethod异步抛出,会在第二个版本的UI线程上抛出。

【讨论】:

    【解决方案3】:

    我建议在这种情况下避免使用事件处理程序,并尽可能使用类似的东西。

     public class Eventful
        {
            private readonly List<Func<Task>> _handlers = new List<Func<Task>>();
    
            public void AddHandler(Func<Task> handler) => _handlers.Add(handler);
    
            private async Task RunHandlers()
            {
                foreach (var handler in _handlers)
                {
                    await handler();
                }
            }
        }
    
        public class Consumer
        {
            public Consumer()
            {
                var eventful = new Eventful();
                eventful.AddHandler(async () =>
                {
                    await DoAsyncTask();
                });
            }
    
            private static async Task DoAsyncTask()
            {
                await Task.CompletedTask;
            }
        }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-19
    • 1970-01-01
    • 1970-01-01
    • 2013-05-26
    • 1970-01-01
    • 2021-05-26
    • 1970-01-01
    • 2021-07-11
    相关资源
    最近更新 更多