2

I have an asynchronous method:

public async Task<Foo> GetFooAsync();

And I need its synchronous version. Something like this:

public Foo GetFoo();

I do not really want to totally rewrite code of GetFooAsync and I would like to do something such as

public Foo GetFoo() 
{
    return GetFooAsync().GetAwaiter().GetResult();
}

Is it good idea or this method has any unobvious problems? As I know if I use GetFooAsync().Result in synchronous context I may face with deadlock. But what about GetFooAsync().GetAwaiter().GetResult()?

Pupkin
  • 1,211
  • 3
  • 13
  • 30
  • 4
    ``return GetFooAsync().Result;`` ? – Ehsan Sajjad Sep 15 '17 at 12:43
  • 1
    Don't use `.Result` use `.GetAwaiter().GetResult()` because the latter handles aggregate exceptions, the former does not. – Lloyd Sep 15 '17 at 12:45
  • 3
    If you want a synchronous version then add a shync overload separately – Rahul Sep 15 '17 at 12:46
  • @Rahul apparently I don't understand. Could you to explain you idea with an example? – Pupkin Sep 15 '17 at 12:48
  • @EhsanSajjad as I know if I use `GetFooAsync().Result` in synchronous context I may face with deadlock. But what about `GetFooAsync().GetAwaiter().GetResult()`? – Pupkin Sep 15 '17 at 12:49
  • 1
    I would agree with Rahul here, don't wrap your async method with a sync call. Bite the bullet and rewrite it. – DavidG Sep 15 '17 at 12:50
  • Have a look at Stephen C's AsyncEx Synchronous Task Extentions: https://github.com/StephenCleary/AsyncEx/blob/master/src/Nito.AsyncEx.Tasks/SynchronousTaskExtensions.cs – Fildor Sep 15 '17 at 12:51
  • @DavidG why I shouldn't wrap async method? – Pupkin Sep 15 '17 at 12:51

3 Answers3

4

Mixing synchronous/async code may lead to deadlocks as described in this article (paragraph 'Async all the way').

Problem is where Task continuation runs and that depends on current SynchronizationContext. If the synchronization is meant to be scheduled on the same thread that's currently blocked by call to Wait()/Result/GetResult() you're running into trouble

orhtej2
  • 2,133
  • 3
  • 16
  • 26
3

If you want a synchronous version then add a shynchronous overload of the method separately in order to have SOC (Separation Of Concern). I.c, add an overload like below as you have already done

public Foo GetFoo() 
{
  ///code body
}
Rahul
  • 76,197
  • 13
  • 71
  • 125
  • In general should a synchronous version be written first, and then modified for async (maybe with flags), or can the synced version simply be wrapped in a worker thread to make in async? – samus Sep 15 '17 at 12:55
  • 3
    @samusarin, No, as stated in answer it's always better to have two different version of the method and the same can be seen even in .NET BCL's – Rahul Sep 15 '17 at 12:56
3

You should not hide an asynchronous implementation behind a synchronously running method, e.g. by using

Task.Run(async () => await GetFooAsync());

Either implement the synchronous version separately or let the consumer explicitly synchronize the GetFooAsync. The problem is, that running the task will consume worker threads and the pool of available workers is limited. You could run into blocking code. So the API consumer should be aware of that the synchronous implementation is asynchronously processed.

mbnx
  • 912
  • 5
  • 11
  • What if you changed name to `public Foo SynchronisedGetFooAsync() `? So that consumers know its just a wrapper for an async method. – Yair Halberstadt Sep 15 '17 at 12:55