避免异步无效

  • 唯一可以安全使用 async void 的地方是事件处理程序。请考虑以下代码:

    private async Task<bool> SomeFuncAsync() {
      ...
      await ...
    }
    public void button1_Click(object sender,  EventArgs e) {
      var result = SomeFuncAsync().Result;
      SomeOtherFunc();
    }
    

    一旦 async 呼叫完成,它将等待 SynchronizationContext 变为可用。但是,事件处理程序在等待 SomeFuncAsync 方法完成时保持 SynchronizationContext; 从而导致循环等待(死锁)。

    要解决这个问题,我们需要将事件处理程序修改为:

    public async void button1_Click(object sender,  EventArgs e) {
      var result = await SomeFuncAsync();
      SomeOtherFunc();
    }
    
  • 任何从 async void 方法抛出的异常将直接在 async void 方法启动时激活的 SynchronizationContext 上引发。

    private async void SomeFuncAsync() {
      throw new InvalidOperationException();
    }
    public void SomeOtherFunc() {
      try {
        SomeFuncAsync();
      }
      catch (Exception ex) {
        Console.WriteLine(ex);
        throw;
      }
    }
    

    这个例外永远不会被 SomeOtherFunc 中的 catch 块捕获。

  • async void 方法没有提供一种简单的方法来通知他们已经完成的调用代码

  • async void 方法很难测试。MSTest 异步测试支持仅适用于返回 TaskTask<T>async 方法。