3

I have a layered architecture and usually put things like try/catches, logging and now async at the top level service layer so my domain code and repositories are written synchronously and then I wrap the calls in my service layer with an await task.run so I can keep the Async naming and logic in one layer. Is this OK? Or is there a reason to have async in all the child methods / lower layers as well? Will it actually spawn more threads or still only be 1 async thread that does the magic?

If there is no issue with this, I have one problem where in my repository I need to use HttpClient but it only has async methods forcing me to use async lower in my architecture where I don't want to. Is there a way to call these methods without await/async and then at a higher layer method (in my service layer) still wrap in a task.run without blocking? Basically I do not want any methods to be async in my repository and would rather them be in my service layer which makes calls to the repository and other domain methods.

Sean Merron
  • 442
  • 4
  • 15
  • 1
    Do not use Task.Run in the implementation of the method; instead, use Task.Run to call the method (from [here](http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html)). – Sinatr Jul 05 '16 at 13:44
  • 2
    In his [blog article](http://blog.stephencleary.com/2012/02/async-and-await.html), Stephen Cleary has made some good points that you may find interesting. – Fabjan Jul 05 '16 at 13:45
  • Thanks @Fabjan but I wasn't able to use this to help figure out my answer. – Sean Merron Jul 05 '16 at 14:02
  • 1
    Why are you using async in the first place? This is a web server scenario, right? – usr Jul 05 '16 at 14:07
  • Thanks @Sinatr but I'm getting mixed up in the blog article to help me with my situation. TI use task.run in my service layer to call repository methods. Problem is if a method within my repository method requires async (HttpClient.PostSync) then I have to force async in my repo. – Sean Merron Jul 05 '16 at 14:08
  • @usr to go out to a data store and perform persistence or get data. – Sean Merron Jul 05 '16 at 14:09
  • 2
    I mean why async and not sync. This is a web server scenario? – usr Jul 05 '16 at 14:09
  • 1
    You may be better off asking this on http://programmers.stackexchange.com/ – rory.ap Jul 05 '16 at 14:29
  • @usr -- Maybe he wants to have a bunch of threads handling web requests so one long-running thread doesn't tie up response time for other requests. – rory.ap Jul 05 '16 at 14:30
  • On most web servers threads are an ample resource so maybe the solution is for him to not use async. That's the easiest solution to the many architectural issues caused by async. I'm trying to find out if that applies here or not. – usr Jul 05 '16 at 14:33
  • By and large the community does not understand when and why async is beneficial. Maybe he was mislead to believe that async provides him benefits that it does not actually provide. I see that all the time. – usr Jul 05 '16 at 14:34
  • @usr -- not a web dev, but do requests automatically get pushed onto separate threads? If so, then I agree with you. – rory.ap Jul 05 '16 at 14:34
  • Are you calling an async HttpClient method in the middle of fulfilling a request to one of your controllers and wondering how to return the result? – raduation Jul 05 '16 at 14:34
  • There are no separate request pools in ASP.NET. Requests and IO completions run on the same thread pool. Usually, there is no queueing to speak of. The queue length almost always is zero or in the frictional range (e.g. 1 or 2). Different requests do not block each other. – usr Jul 05 '16 at 14:36
  • Well then I agree. – rory.ap Jul 05 '16 at 14:37
  • 1
    @roryap when referring other sites, it is often helpful to point that [cross-posting is frowned upon](http://meta.stackexchange.com/tags/cross-posting/info) – gnat Jul 05 '16 at 14:44
  • *Is there a way to call these methods without await/async and then at a higher layer method (in my service layer) still wrap in a task.run without blocking*. What your talking about here is the [sync over async anti-pattern](http://stackoverflow.com/a/32606739/542251) I've found it to be a necessary evil in these scenarios. – Liam Jul 05 '16 at 14:49
  • yep @Liam that's what I'm thinking.. ugly but I like the separation but wanted to make sure there are no other drawbacks. – Sean Merron Jul 05 '16 at 15:44
  • @usr you do have me wondering if I should be using async at all for an asp.net web api with multiple layers under it (service, domain, repositories). It is still all within one request and may be better to just block in the request context. Why the heck would I want to use async then at all other than maybe not blocking a UI thread? I swear I saw an article on why async was still good to use in ASP.NET I'll have to find it again. Maybe the context switching and request disconnect loss costs isn't worth the freeing of threads temporarily for other requests. – Sean Merron Jul 05 '16 at 19:05
  • @raduation my Web API is making a call to another Web API using httpclient which only has PostAsync. But now I wonder if I just block that PostAsync call with a wait and make my whole service not use async at all. – Sean Merron Jul 05 '16 at 19:06
  • The current propaganda is to always use async. That is very questionable advice. Like many others you fell for the the mainstream opinion. Do not be blinded by phrases such as "IO bound" and "scalable". Ask what concrete benefit the project will derive from async. I have written answers about how to calculate with numbers whether you need async or not. If not you can indeed block on PostAsync. I have done that. – usr Jul 05 '16 at 19:21
  • @usr If my web api is going out to another resource for some data. It's more performant to do it asynchronously right? Or are you suggesting to just block those operations synchronously behind the API? – Sean Merron Jul 06 '16 at 10:56
  • The HTTP call clearly does not get faster by using a different call style. Making an async call and then blocking on it is the worst case from a perf standpoint but it causes a tiny incremental amount of CPU usage compared to the call. It's just not a concern in most cases. Try it and attach a profiler. – usr Jul 06 '16 at 11:05

1 Answers1

3

Is this OK?

Nope, not ok.

According to a very well written series of Stephen Cleary blog posts:

There are (at least) four efficiency problems introduced as soon as you use await with Task.Run in ASP.NET:

  • Extra (unnecessary) thread switching to the Task.Run thread pool thread. Similarly, when that thread finishes the request, it has to enter the request context (which is not an actual thread switch but does have overhead).
  • Extra (unnecessary) garbage is created. Asynchronous programming is a tradeoff: you get increased responsiveness at the expense of higher memory usage. In this case, you end up creating more garbage for the asynchronous operations that is totally unnecessary.
  • The ASP.NET thread pool heuristics are thrown off by Task.Run “unexpectedly” borrowing a thread pool thread. I don’t have a lot of experience here, but my gut instinct tells me that the heuristics should recover well if the unexpected task is really short and would not handle it as elegantly if the unexpected task lasts more than two seconds.
  • ASP.NET is not able to terminate the request early, i.e., if the client disconnects or the request times out. In the synchronous case, ASP.NET knew the request thread and could abort it. In the asynchronous case, ASP.NET is not aware that the secondary thread pool thread is “for” that request. It is possible to fix this by using cancellation tokens, but that’s outside the scope of this blog post.

This should be more than enough warning to reconsider your approach. "The fact that you're using Task.Run for asynchronous wrappers is a code smell"

Is there a reason to have async in all the child methods / lower layers as well?

Yes, etiquette.

Will it actually spawn more threads or still only be 1 async thread that does the magic?

There is no thread.

I suggest reading the best practices.

Additional reading by Stephen Toub.

David Pine
  • 23,787
  • 10
  • 79
  • 107
  • Thanks for the reply @david-pine. However now you make me question using async at all for any operations behind an ASP.NET web API and all layers under (Service, Domain, Repositories). Is there truly a benefit to using it unless I'm trying to either A) unlock a UI thread or B) run multiple operations in a task array? My Task.Run wrapper was creating 1 context switch whereas I could have many switches and performance hits if I let async flow throughout and lost Request thread disconnect benefits. Why not just block everything behind a Web API method call? – Sean Merron Jul 05 '16 at 19:00
  • Absolutely, using `async` in a Web API is the ideal use-case for it. For one, you're in the context of an **I/O** bound operation, either pulling data from some data store and returning over the wire. Both of these are bottlenecks and can cause performance implications. You should be using "async all the way" here. http://stackoverflow.com/a/23928922/2410379, http://stackoverflow.com/a/22178599/2410379 and http://stackoverflow.com/a/16841486/2410379 – David Pine Jul 05 '16 at 19:04
  • Thanks @david-pine. So if I understand correctly, good to use async/await for I/O bound operations but not good to use a Task.Run for those. Right? – Sean Merron Jul 06 '16 at 10:54
  • @SeanMerron correct. – David Pine Jul 06 '16 at 11:05
  • Thanks @david-pine for taking the time to clear me up! I really appreciate it and it's time to make some code changes :-) – Sean Merron Jul 06 '16 at 11:54