26

I have this library which is purely sync. It exposes sync methods and I have clients using it.

I changed the underlying implementation to async and exposed async methods for whoever wants to use it. But now I have lots of replicated code. Async code seems to perform better. I'd like existing clients to take advantage of it and I want to eliminate code repetition.

Is there any safe way to keep a sync signature and call async implementation?

I'm specifically afraid of deadlocks when calling .Result and .Wait.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Igor Gatis
  • 4,648
  • 10
  • 43
  • 66
  • 6
    You really shouldent, If you do you are going to end up degrading sync performance. The best way to do it is use a fully sync path for sync API calls and a fully async path for async calls. Refactor the code that does not rely on sync or async along the paths where you can and have both paths call the function. – Scott Chamberlain Jun 18 '14 at 23:17

2 Answers2

29

I strongly encourge you not to do this

First, read Should I expose synchronous wrappers for asynchronous methods? and Should I expose asynchronous wrappers for synchronous methods? by Stephan Toub.

The main reasons I wouldn't do this:

  1. Sync over Async - As you said, deadlocks. Higher or lower down the call chain, using Result or Wait on async maybe risky business. It really depends on which platform you run (ASP.NET, UI, Console) as each one behaves a bit differently (Even when using ConfigureAwait(false))

  2. Async over Sync - Scalability. Once I see an async endpoint, I assume it is pure async, which for me, as the consumer of the API means there isn't any Thread spinning "behind my back". If your users assume the same, finding out that for every call to an async method a Thread Pool thread is used can drastically hurt performance when trying to scale out. If the users want to wrap a sync method with a Task.Run, leave it up to them to make that call and make their on judgement on how this will affect their application

howcheng
  • 2,211
  • 2
  • 17
  • 24
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • 12
    Sounds like C# async feature is half baked, IMO. There is no "adoption" strategy. And saying "duplicate..." is not an answer. – Igor Gatis Mar 28 '16 at 11:26
  • 2
    C# async feature is very much baked, IMO. You're right, there is no "easy way out", but that's a design decision, doesn't make it any less "baked". Your code base will need to adapt to async. – Yuval Itzchakov Mar 28 '16 at 11:27
  • 1
    By "half-baked" I meant adoption-wise. Adoption strategy is key for ecosystem growth: the smaller the community, the larger is the chance nobody will exercise its features, catch bugs, etc. – Igor Gatis Mar 28 '16 at 17:57
  • @Gatis, I see. I think the amount of adopters is quite broad TBH. If only by the amount of SO questions regarding async-await. – Yuval Itzchakov Mar 28 '16 at 18:00
  • 2
    You said either way is not an option, but you never specified an option for when you HAVE to do this. Say you have a contract that an async and sync class need to adhere to, which way would you go to fulfill that contract, sync or async? – Francisco Aguilera Dec 30 '16 at 17:43
  • @FranciscoAguilera That really depends on the underlying implementation of that contract. If, for example, the contract requires me to make an HTTP call to some 3rd party service, I'd use `WebClient` for sync and `HttpClient` for async. The OPs question isn't about being forced to obey a contract, it's a general question about mixing sync and async. There isn't an ultimate solution to that problem, it varies with the situation. – Yuval Itzchakov Dec 30 '16 at 18:49
  • @YuvalItzchakov True, see if you can chime in on [my question](http://stackoverflow.com/questions/41401045/contractual-async-and-sync-code/41401157). – Francisco Aguilera Dec 30 '16 at 18:51
4

Try to start from the bottom up, or you run into deadlocks (

Console apps behave differently from web and UI apps as far as how they handle deadlocks if they're NOT properly handled. If you're using MVC, use an async task ActionResult and wrap specific synchronous calls on Task.Run(() => SYNCCODE) using async and await. Similar process with UI code, using async/await upon event methods (such as Click event handlers).

I typically wrap my sync calls with async versions and handle them as tasks. If those sync methods can utilize Async versions of .NET methods, I try to go "deeper in" where possible.

Ryan Peters
  • 7,608
  • 8
  • 41
  • 57