1

In learning about asynchronous programming, I have been trying to implement an interface that applies to both Asynchronous and Synchronous classes, but I am seeing conflicting practices.

So take for example, if I was trying to implement an ILight Interface, with methods On() and Off().

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

With a WiredLight, the methods are synchronous, and is all quick CPU bound work.

public class WiredLight : ILight
{
    private bool _powered;

    public void On()
    {
        _powered = true;
    }

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

But with a WirelessLight, the methods are Asynchronous, where there is IO bound work. (Here the methods do not follow the interface signature, but are implemented as async, to avoid async void.)

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

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

Reading around (here and here), the way to do this is just force asynchronous signatures in the interface, then all the synchronous calls would be refactored to be asynchronous (Ref: Async all the way). This sounds fine to me, but I haven't really seen anything about how the synchronous methods (from WiredLight) should be handled. Unlike this question, there are no IO bound operations, so there is nothing to await. I considered wrapping them in an asynchronous call (return Task.Run(() => { _powered = true; });), but this is contrary to most recommendations. I also considered simply returning an already completed task(Similar to this or this)

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

But presenting the method as Async when runs synchronously is lying, and also against reccomendations. In addition, it looks like returning a Task.CompletedTask may return the same Task to different calls. I can't tell where this would cause problems at the moment, but it seems like something that would cause problems.

Is there an accepted practice for implementing an async interface on a method that returns nothing and really should be synchronous?

  • What makes you want to write this interface as async? – zaitsman May 25 '18 at 05:29
  • "I also considered simply returning an already completed task" -- yup, that's the right thing to do. I've always used `Task.FromResult()` instead of `CompletedTask`, if you're worried about task object identity then `FromResult()` may solve that. – Ben Voigt May 25 '18 at 05:34
  • @zaitsman Using the example above, I already had a `WiredLight` that implemented `ILight` and I just added `WirelessLight` which also needed to implement `ILight`. (Basically I am in the midst of the Async Zombie virus) – chipstheninja May 25 '18 at 18:38

1 Answers1

1

Doing something asynchronous from a synchronous method can be really dangerous. There's no perfect way of doing that, and you may often run into deadlock of thread-starvation issues. That's not a situation you want to find yourself in.

On the other end, doing something synchronous from an asynchronous method is safe. Sure, you're lying to the caller, but that's not really a problem unless he wanted execute your code in parallel (in which case, he'll simply add a Task.Run once he notices the issue, that you should also mention in the comments). When awaiting a completed task, the runtime is smart enough to optimize away the call, so the performance impact for the caller is very small, negligible for most applications.

So if you have a reason to think that you'll need to execute asynchronous code in one of the implementations of your interface, don't hesitate and mark it asynchronous. For the implementations that are actually synchronous, returning a completed task is usually the best thing to do:

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

I would advise against using Task.Run inside of the method, as it would often be undesirable and can be added easily by the caller if needed. async in .net means that the method may finish asynchronously, not that it'll return quickly.

Kevin Gosse
  • 38,392
  • 3
  • 78
  • 94