【问题标题】:async Task method that doesn't need to await不需要等待的异步任务方法
【发布时间】:2018-02-23 20:23:00
【问题描述】:

我有一组从基类继承的命令。基类有以下声明:

public virtual async Task Execute(object parameter){}

继承类提供了对这个方法的覆盖,并不是所有的类都在等待任务。在这些情况下,编译器会生成警告:

此异步方法缺少“等待”运算符,将同步运行。 考虑使用 'await' 运算符来等待非阻塞 API 调用, 或 'await Task.Run(...)' 在后台线程上执行 CPU 密集型工作。

明确提供任务完成返回值是否正确?

public override async Task Execute(object parameter) {
    //code containing no await statements...
    await Task.CompletedTask;
}

【问题讨论】:

    标签: c# async-await


    【解决方案1】:

    您应该避免在重写的方法中使用 async 关键字,这些方法不会在任何东西上使用 await,而是只返回 Task.CompletedTask:

    public override Task Execute(object parameter) {
        //code containing no await statements...
        return Task.CompletedTask; // or Task.FromResult(0) for older .NET versions
    }
    

    这是此类“模拟”任务的(少数)用例之一,您可能通过looking at this question 了解Task.FromResult。这些注意事项对于Task.CompletedTask 仍然有效。

    【讨论】:

    • 我将原始代码的输出和您推荐的 dotPeek 更改的输出进行了比较,它们是相同的(调试 dll)。您的示例也删除了编译器警告,这是我最初的动机。 - 谢谢!
    【解决方案2】:

    作为对比,处理异常的方式有所不同。

    如果从非async 版本中的同步代码引发异常,则该异常将直接引发给调用者(对于具有异步签名的方法来说非常不寻常)。

    如果async 版本中的同步代码引发异常,则会捕获该异常并将其放置在返回的任务上(这是具有异步签名的方法的预期行为)。

    所以,如果这样调用方法,不同的实现会有不同的异常语义:

    var task = Execute(parameter); // non-async exceptions raised here
    ...
    await task; // async exceptions raised here
    

    不过,大多数时候,该方法被调用并立即等待,因此这两个语义合并在一起:

    await Execute(parameter); // both async and non-async exceptions raised here
    

    可能无关紧要,但我相信这是一个需要注意的重要区别。

    如果异常语义对您很重要,那么您确实想使用async 围绕您的同步代码生成状态机,您可以在代码中禁用警告: p>

    #pragma warning disable 1998 // use async keyword to capture exceptions
    public override async Task Execute(object parameter) {
    #pragma warning restore 1998
      //code containing no await statements...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 2014-03-18
      • 1970-01-01
      相关资源
      最近更新 更多