在学习异步编程时,我一直在尝试实现一个适用于异步和同步类的接口,但我看到了相互矛盾的做法。
例如,如果我尝试使用方法On()和Off()来实现ILight
接口。
public interface ILight
{
void On();
void Off();
}
使用WiredLight
时,这些方法是同步的,并且都是快速的CPU绑定工作。
public class WiredLight : ILight
{
private bool _powered;
public void On()
{
_powered = true;
}
public void Off()
{
_powered = false;
}
}
但是使用WirelessLight
,方法是异步的,其中有IO绑定工作。 (这里的方法不遵循接口签名,但实现为async,以避免异步void。)
public class WirelessLight : ILight
{
public async Task On()
{
await EnablePowerAsync();
}
public async Task Off()
{
await DisablePowerAsync();
}
}
阅读(here和here),这样做的方法只是强制接口中的异步签名,然后所有同步调用都将被重构为异步(Ref:{{3 }})。这对我来说听起来不错,但我还没有真正看到有关如何处理同步方法(来自WiredLight
)的任何内容。与Async all the way问题不同,没有IO绑定操作,因此无需等待。我考虑过将它们包装在异步调用中(return Task.Run(() => { _powered = true; });
),但这与大多数this相反。我还考虑过简单地返回已完成的任务(类似于recommendations或this)
public class WiredLight : ILight
{
public Task OnAsync()
{
_powered = true;
return Task.CompletedTask;
}
}
但是在同步运行时将方法显示为Async是撒谎,也是针对建议。此外,看起来返回Task.CompletedTask
可能会将相同的任务返回给不同的调用。我不知道这会在什么时候造成问题,但它似乎会引起问题。
在什么都不返回并且确实应该同步的方法上实施异步接口是否有公认的做法?
答案 0 :(得分:2)
使用同步方法执行异步操作可能非常危险。没有完美的方法,你可能经常陷入线程饥饿问题的僵局。这不是你想要找到的情况。
另一方面,从异步方法执行同步操作是安全的。当然,你对调用者撒谎,但除非他想并行执行你的代码,否则这不是一个问题(在这种情况下,他只需添加Task.Run
一次注意到这个问题,你也应该在评论中提到)。在等待已完成的任务时,运行时足够智能以优化呼叫,因此对呼叫者的性能影响非常小,对大多数应用程序来说可忽略不计。
因此,如果您有理由认为您需要在界面的某个实现中执行异步代码,请不要犹豫并将其标记为异步。对于实际上是同步的实现,返回已完成的任务通常是最好的事情:
public Task DoSomethingAsync()
{
DoSomething();
return Task.CompletedTask;
}
我建议不要在方法中使用Task.Run
,因为它通常是不受欢迎的,并且如果需要可以由调用者轻松添加。 .net中的async
表示该方法可以异步完成,而不是它会快速返回。