C# 在同步对象中实现异步接口

C# 在同步对象中实现异步接口,c#,.net,asynchronous,interface,async-await,C#,.net,Asynchronous,Interface,Async Await,在学习异步编程的过程中,我一直在尝试实现一个既适用于异步类又适用于同步类的接口,但我看到了相互冲突的实践 举个例子,如果我试图实现一个ILight接口,方法是On()和Off() 公共界面ILight { 在()上无效; 作废(); } 使用WiredLight,这些方法是同步的,并且都是快速的CPU工作 公共类WiredLight:ILight { 私人厕所供电; 在()上公开作废 { _动力=真; } 公众假期 { _加电=假; } } 但是对于WirelessLight,方法是异步的,

在学习异步编程的过程中,我一直在尝试实现一个既适用于异步类又适用于同步类的接口,但我看到了相互冲突的实践

举个例子,如果我试图实现一个
ILight
接口,方法是On()和Off()

公共界面ILight
{
在()上无效;
作废();
}
使用
WiredLight
,这些方法是同步的,并且都是快速的CPU工作

公共类WiredLight:ILight
{
私人厕所供电;
在()上公开作废
{
_动力=真;
}
公众假期
{
_加电=假;
}
}
但是对于
WirelessLight
,方法是异步的,其中存在IO绑定的工作。(这里的方法不遵循接口签名,而是作为异步实现的,以避免异步无效。)

公共类WirelessLight:ILight
{
()上的公共异步任务
{
等待启用PowerAsync();
}
公共异步任务关闭()
{
等待禁用PowerAsync();
}
}

仔细阅读(和),这样做的方法就是在接口中强制使用异步签名,然后所有同步调用都将被重构为异步(Ref:)。这听起来不错,但我还没有真正了解到同步方法(来自
WiredLight
)应该如何处理。和问题不同的是,并没有IO绑定的操作,所以并没有什么可以等待的。我考虑过将它们包装在一个异步调用(
returntask.Run(()=>{u-powered=true;});
)中,但这与大多数情况相反。我还考虑过简单地返回一个已经完成的任务(类似于或)

公共类WiredLight:ILight
{
公共任务同步()
{
_动力=真;
返回Task.CompletedTask;
}
}
但是,在同步运行时将该方法表示为异步是错误的,而且也是针对RecomEndation的。此外,它看起来像是在返回一个
任务。CompletedTask
可能会将同一个任务返回给不同的调用。我现在不知道这会在哪里引起问题,但它似乎会引起问题


对于不返回任何内容且确实应该是同步的方法,是否有一种公认的做法来实现异步接口。没有完美的方法可以做到这一点,而且您可能经常遇到线程匮乏问题的死锁。这不是你想要的情况

另一方面,从异步方法执行同步操作是安全的。当然,你在对调用者撒谎,但除非他想并行执行你的代码,否则这不是一个真正的问题(在这种情况下,他只需添加一个
任务。一旦他注意到问题,就运行
,你也应该在注释中提及)。在等待完成的任务时,运行时足够智能,可以优化调用,因此对调用方的性能影响非常小,对于大多数应用程序来说可以忽略不计

因此,如果您有理由认为需要在接口的某个实现中执行异步代码,请不要犹豫,将其标记为异步。对于实际同步的实现,返回已完成的任务通常是最好的:

public Task DoSomethingAsync()
{
    DoSomething(); 
    return Task.CompletedTask;
}

我建议不要在方法内部使用
Task.Run
,因为它通常是不受欢迎的,如果需要,调用方可以轻松添加它async
意味着该方法可以异步完成,而不是快速返回。

是什么让您希望将此接口编写为异步的?“我还考虑过简单地返回已完成的任务”--是的,这是正确的做法。我一直使用
Task.FromResult()
而不是
CompletedTask
,如果您担心任务对象标识,那么
FromResult()
可能会解决这个问题。@zaitsman使用上述示例,我已经有一个
WiredLight
实现了
ILight
,我刚刚添加了
wireleslight
,它也需要实现
ILight
。(基本上我正处于异步僵尸病毒之中)