2

I'm using one httpclient instance to send multiple requests to rest web api to get data. Here is what my code looks like:

First I have a control layer that calls the data layer for data.

public class ControlLayer
{
    protected DataLayer dal;

    //constructors here

    public int getInfo1(int param)
    {
      int ret = this.dal.getInfo1(param);
      return ret;
    }

    public int getInfo2(int param)
    {
      int ret = this.dal.getInfo2(param);
      return ret;
    }
}

then I have the dataLayer that calls webAPI, it looks like this. here for simplicity, im using .result directly.

public class DataLayer
{
    HttpClient client = new HttpClient();
    string url = "";

    public int getInfo1(int param1)
    {
      int ret=0;
      HttpResponseMessage response = client.GetAsync(url).Result;
      //.... do some work, get the value for ret

      return ret;
    }

    public int getInfo2(int param1)
    {
      int ret = 0;
      HttpResponseMessage response = client.GetAsync(url).Result;
      //.... do some work, get the value for ret

      return ret;
    }
}

my questions is I've seen some tutorials saying that we should avoid using .result, because it might lead to deadlock. I'm not sure in my case do I need to use async/await? if I do need, I know I should async all the way down, but I do want my controlLayer to be sync, because I have other layer that calls the controlLayer's function, I don't want all the layer's function to be async and the result be Task<>, is this a situation of sync over async? am I miss something? any suggestions are appreciated. thanks!

Vicky
  • 23
  • 1
  • 1
  • 3
  • If you don't want code to be async (is it good or bad - your personal call) than don't make async calls. Not sure what your problem with that... – Alexei Levenkov Dec 03 '13 at 19:18
  • thanks for your quick reply, are you suggesting using the .result function directly? will that lead to deadlock? – Vicky Dec 03 '13 at 19:23
  • @AlexeiLevenkov HttpClient has an async API, so you must either use `Result` or `await` the results. – sinelaw Dec 03 '13 at 19:23
  • `HttpClient` is not the only API there... If one prefers synchronous code `WebRequest` may be another option... But it all depends on if author can control where the code is used (see links in sinelaw's answer +1) - if it is console apps synchronous waiting for async methods is ok, otherwise care must be taken to avoid deadlock. – Alexei Levenkov Dec 03 '13 at 19:32
  • @AlexeiLevenkov HttpClient might not be the only choice, but I still find it good enough that I **don't** want to use others. How do I make HttpClient work *just* like WebClient (synchronously), *without* the risk of deadlocks? (I still want to block the UI thread until the request completes, though). – Dexter Feb 06 '17 at 15:54
  • @Dexter deadlock perfectly satisfy your requirement - "block the UI thread till task completes". More seriously - if you like HttpClient API you may want to spend some time to implementing your own class with similar methods but using WebRequest to make requests. I'd strongly recommend reading Stephen's https://msdn.microsoft.com/en-us/magazine/mt238404.aspx article and if you have some time just read through all his aswers on SO - http://stackoverflow.com/users/263693/stephen-cleary. (You'd better ask separate question) – Alexei Levenkov Feb 06 '17 at 18:21
  • @AlexeiLevenkov "deadlock perfectly satisfy your requirement" - blocking the UI thread until task completes is not a deadlock. Deadlock means it's blocked **forever**. – Dexter Mar 19 '17 at 08:05
  • @Dexter deadlocking in this case prevents task from completion - so it achieves your goal as stated. (Please note that it *was not* serious suggestion to use - just trying to highlight that blocking UI thread for undefined amount of time is questionable... Network operations are know to take seconds and default timeout is ~30 second - most people will consider UI frozen for 30 seconds as dead program and kill it). – Alexei Levenkov Mar 20 '17 at 15:25
  • Nevermind, in the meantime I finally realized what is `await` used for - it does wait for the async operation to complete, making my code execute like it was synchronous - except it waits _asynchronously_ and does not even block the UI thread (I always thought 'await' blocks the UI thread!) And after some reading I realized that the source of deadlock, in most cases, _literally_ **is** the blocking of the UI thread. **P.S.** Of course my goal was _not_ to prevent task to complete - but to prevent any _other_ code from executing until it completes. – Dexter Jun 28 '17 at 11:55

2 Answers2

7

I do want my controlLayer to be sync, because I have other layer that calls the controlLayer's function, I don't want all the layer's function to be async and the result be Task<>

I recommend you rethink this. A web request is a fundamentally asynchronous operation, so I recommend that you expose your "control layer" as an asynchronous API and allow the async to "grow" through the layers in your code base.

However, if you really want a synchronous API, then you should only call synchronous APIs. E.g., use WebClient instead of HttpClient. Do not call Result to wrap an asynchronous API with a synchronous API.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • @LenielMacaferi: I disagree with that answer. On some platforms, calling `Result` on an `HttpClient` async API will cause a deadlock. – Stephen Cleary Apr 14 '14 at 23:56
  • @StephenCleary so, if Result could cause a deadlock, how can I still use HttpClient without the risk of deadlock? I WANT it all to be synchronous, literally just like the way WebClient works, while still using the new, more advanced HttpClient. – Dexter Feb 06 '17 at 15:52
  • @Dexter: `HttpClient` is async-only. `WebClient` supports synchronous and asynchronous code. – Stephen Cleary Feb 06 '17 at 16:39
  • @StephenCleary and how exactly does WebClient work in synchronous mode? "A web request is a fundamentally asynchronous operation", so the synchronous functions in WebClient probably have some code that awaits. And I doubt using WebClient's synchronous methods could ever produce a deadlock (Microsoft is probably smart enough). So, how do I do this myself with HttpClient? – Dexter Mar 19 '17 at 08:08
  • @Dexter: `WebClient` blocks in its synchronous calls. You *could* call `HttpClient`'s async methods and then call `GetAwaiter().GetResult()` on the returned task. **However,** on some platforms this will deadlock because of `HttpClient`'s implementation. It simply wasn't designed for that scenario. – Stephen Cleary Mar 19 '17 at 13:01
5

You only need to use async and await if your code is asynchronous - for example if it dispatches several requests simultaneously, doing work after a request is sent instead of just blocking until the response arrives.

Ignoring the deadlock issue for a moment - if your code is simply synchronous, that is: every time you send a request you just wait for the response before doing anything else, you don't need to use await and can use Result. See this question for a similar debate. Alternatively, you can use a synchronous API (such as WebClient as suggested in the comments and other answer).

As for Result-related deadlocks, I recommend you read this article on MSDN for a better understanding of what happens and why. If you're writing a simple console application, you don't really need to worry about it (the right way to deal with it is to have only your Main method non-async, and use Result or Wait there).

Community
  • 1
  • 1
sinelaw
  • 16,205
  • 3
  • 49
  • 80
  • thanks! in my case, I have a clientLayer sending the requests parameters, can be simultaneously, and after I get the response I will send the result to the clientLayer, so I think I do need the async and await, but do you have any idea how to deal with the sync over async? – Vicky Dec 03 '13 at 19:36
  • @Vicky, read the article - one of the 'best practices' they recommend is 'Async all the way' - have all your code be async all the way to the highest possible level, and use Result and Wait() there. – sinelaw Dec 03 '13 at 19:37