不需要等待的异步任务方法

5

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

public virtual async Task Execute(object parameter){}

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

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

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

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

11

在不等待任何东西的重写方法内避免使用async关键字,而是返回Task.CompletedTask

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

这是使用“mocked”任务的(少数)用例之一,你可以通过查看此问题来理解Task.FromResult的用途。 这些考虑因Task.CompletedTask而仍然有效。


2
我对我的原始代码和你从dotPeek推荐的更改后的输出进行了比较,它们是相同的(调试dll)。你的示例还消除了编译器警告,这也是我最初的动机。-谢谢! - jchristof

7
作为对比,处理异常的方式确实有所不同。
如果在非 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在同步代码周围生成状态机,并可以在代码中禁用警告:

#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...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接