提问者:小点点

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


null

例如,如果我试图用On()和Off()方法实现一个接口。

public interface ILight
{
    void On();
    void Off();
}

使用,这些方法是同步的,并且都是快速的CPU绑定工作。

public class WiredLight : ILight
{
    private bool _powered;

    public void On()
    {
        _powered = true;
    }

    public void Off()
    {
        _powered = false;
    }
}

但是使用,这些方法是异步的,其中有IO绑定的工作。(这里的方法不遵循接口签名,而是作为异步实现,以避免异步无效。)

public class WirelessLight : ILight
{
    public async Task On()
    {
        await EnablePowerAsync();
    }

    public async Task Off()
    {
        await DisablePowerAsync();
    }
}

阅读(这里和这里),这样做的方法只是强制接口中的异步签名,然后所有的同步调用将被重构为异步(ref:Async all the way)。这听起来不错,但我还没有真正了解应该如何处理同步方法(来自)。与这个问题不同的是,没有IO绑定操作,因此没有什么可等待的。我考虑过将它们包装在异步调用中(codeReturn task.run(()=&>{_powered=true;)/code),但这与大多数建议相反。我还考虑过简单地返回一个已经完成的任务(类似于这个或这个)

public class WiredLight : ILight
{
    public Task OnAsync()
    {
        _powered = true;
        return Task.CompletedTask;
    }
}

但是在同步运行时将方法表示为异步是错误的,而且也不利于重新协作。此外,看起来返回一个可能会将相同的任务返回给不同的调用。我现在不知道这在哪里会引起问题,但它似乎是一些会引起问题的东西。

在一个不返回任何东西,实际上应该是同步的方法上实现一个异步接口,有没有一个公认的实践?


共1个答案

匿名用户

从同步方法中执行异步操作可能非常危险。没有完美的方法可以做到这一点,而且您可能经常会陷入线程不足问题的僵局。这不是你想要的情况。

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

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

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

我建议不要在方法内部使用,因为它通常是不希望的,而且如果需要,调用方可以很容易地添加它。NET中的意味着该方法可以异步完成,而不是快速返回。