Calling HttpClient async functions from constructors in ASP.NET 4.5
Recently, we have been using
As it has been documented and explained, in the current ASP.NET stack, when you await during a request, you will get a callback on the originating thread (that's the point of
In the mean time, I hope this will get a few people out of a tough spot.
async/await
a lot in our ASP.NET MVC project. It is very powerful. However, as with most power, comes great responsibility. In multithreading, this adage can be translated to "with great parallelism, don't deadlock".As it has been documented and explained, in the current ASP.NET stack, when you await during a request, you will get a callback on the originating thread (that's the point of
await/async
after all). When you make an awaitable call to let's say HttpClient.GetStringAsync()
, it will want to come back on that thread also. As well explained by Stephen Cleary in his StackOverflow answer (here), this goes south pretty much immediately if you start blocking somewhere in the call stack. This is true if you call a Task<T>.Result
.No async in constructors
Unfortunately, for good reasons, constructors cannot be async. Therefore, they cannot await on anything. In our case, we needed to retrieve some data from another service within a constructor to initialize some configuration. We are making extensive use ofMEF
, so we pretty much let the container resolve what it needs for us. This was an issue as there is no way to make a synchronous call to HttpClient
to get data.Solution
Fortunately, instead of forcing the issue and make the all to HttpClient.GetStringAsync() blocking by using Task<T>.Result, we went the opposite direction and made the call twice asynchronous:var task = Task.Factory.StartNew(()=>client.GetStringAsync("url").Result); var data = task.Result;If I get this right, the reason this works is because the first line will start a new task on a separate thread. It is then on that second thread that the
GetStringAsync
will callback, leaving the main thread available for the outgoing call. If you think I am way off and have a good explanation, please share. I am not an expert on the subject at this time.In the mean time, I hope this will get a few people out of a tough spot.
Hi Eric -
ReplyDeleteI just wrote a blog post specifically addressing async constructors yesterday: http://nitoprograms.blogspot.com/2013/01/async-oop-2-constructors.html
Your solution works but has the disadvantage of blocking two threads while the HTTP operation is in progress; this may not scale well.
Since you're using MEF, I recommend using the "asynchronous initialization pattern" I described yesterday in my blog post.
Best regards,
-Steve
Thanks Steve, I'll take a look. I guess I need to blog more often. Feedback is great!
Delete