【问题标题】:Implementing Asynchronous Interfaces in Synchronous objects在同步对象中实现异步接口
【发布时间】:2018-11-04 10:10:05
【问题描述】:

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

举个例子,如果我尝试使用 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 绑定工作。 (这里的方法不遵循接口签名,而是以异步方式实现,以避免异步无效。)

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

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

阅读(here 和here),这样做的方法只是在接口中强制异步签名,然后所有同步调用将被重构为异步(参考:Async all the way)。这对我来说听起来不错,但我还没有真正看到有关如何处理同步方法(来自WiredLight)的任何信息。与this 问题不同,没有IO 绑定操作,因此没有什么可以等待的。我考虑将它们包装在异步调用中(return Task.Run(() => { _powered = true; });),但这与大多数recommendations 相反。我也考虑过简单地返回一个已经完成的任务(类似于this 或this)

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

但是在同步运行时将方法呈现为异步是谎言,并且也反对推荐。此外,看起来返回 Task.CompletedTask 可能会将相同的任务返回给不同的调用。目前我不知道这会在哪里引起问题,但似乎会引起问题。

对于不返回任何内容并且确实应该同步的方法实现异步接口,是否存在公认的做法?

【问题讨论】:

  • 是什么让你想把这个接口写成异步的?
  • “我也考虑过简单地返回一个已经完成的任务”——是的,这是正确的做法。我一直使用Task.FromResult() 而不是CompletedTask,如果您担心任务对象身份,那么FromResult() 可能会解决这个问题。
  • @zaitsman 使用上面的示例,我已经有一个实现ILight 的WiredLight,我刚刚添加了WirelessLight,它还需要实现ILight。 (基本上我在 Async Zombie 病毒中)

标签: c# .net asynchronous interface async-await


【解决方案1】:

从同步方法执行异步操作可能真的很危险。没有完美的方法可以做到这一点,而且您可能经常遇到线程饥饿问题的死锁。这不是你想要发现自己的情况。

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

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

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

我建议不要在方法内部使用Task.Run,因为它通常是不可取的,并且可以在需要时由调用者轻松添加。 .net 中的async 表示该方法可以异步完成,而不是它会很快返回。

【讨论】:

    猜你喜欢
    • 2016-01-01
    • 2013-01-05
    • 2015-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多