2

I have been a staunch advocate of never blocking on async code. I felt it was always better to use a synchronous API than to run the less efficient state machine generated by the compiler even if there is no chance of deadlock.

But in the specific case of System.Net.HttpClient with code that cannot go async all the way down (like a console app), isn't it better to take advantage of the connection caching of HttpClient than to use something like WebRequest that must negotiate the TCP session on each invocation?

I'm starting to think the benefits of re-using HttpClient, even if you block with .Result, outweigh the reasons for using a synchronous API instead.

This is assuming of course that the HttpClient is a shared instance, would be re-used and is operating in a context-free environment like a console app or asp.net core.

2
  • wait... which way do you think is best again?
    – Ewan
    Aug 12, 2018 at 13:31
  • @Ewan please see edit, is that more clear?
    – Crowcoder
    Aug 12, 2018 at 13:54

2 Answers 2

5

You don't need to block in a console app anymore.

https://stackoverflow.com/questions/9208921/cant-specify-the-async-modifier-on-the-main-method-of-a-console-app

Even when you did, you could just call a MainAsync() from Main and block on that. There's no need to block on a HttpClient call and you shouldn't.

I felt it was always better to use a synchronous API than to run the less efficient state machine generated by the compiler

Your feeling is wrong. Even if the framework you are working in doesn't reuse the thread while you are awaiting, you can still make use of the thread while you await.

4
  • 1
    Async main is still blocking. It is just syntactic sugar provided because it is so often done incorrectly. It still forces everything to execute synchronously unless you force a thread, with Task.Run for instance, and I know I do not want to do that.
    – Crowcoder
    Aug 12, 2018 at 14:56
  • This is absolutely the correct answer for a CLI app. You still go async all the way down. It’s just that in this case there’s no framework making the final blocking call for you, you have to do it yourself. Here’s an example of how. github.com/rubberduck203/GitNStats/blob/master/src/gitnstats/…
    – RubberDuck
    Aug 12, 2018 at 15:05
  • @RubberDuck that still forces the code to run synchronously if you await anything, which is necessary if you need the result. My question is not how to run it asynchrounously, my question is "is it ok to block in this circumstance".
    – Crowcoder
    Aug 12, 2018 at 15:16
  • 2
    @Crowcoder this isn’t running all of the different tasks synchronously, just the only one you have to. You have no idea how any of the async methods are implemented. Many of them might be spinning up threads under the hood. Doing it as I showed you only blocks the exit of the program. Being we have to wait in order to know if the execution was successful, yes, it’s fine to block on that last call.
    – RubberDuck
    Aug 12, 2018 at 15:20
0

Personally, I find it easier to understand and reason about threaded code than async code (not directly contradicting your inclination, but suggesting I may have a different bias).

I'm not sure what you mean about HttpClient not going 'async all the way down'. I haven't looked at the .net implementation of HttpClient, but there is no reason it cannot be implemented async all the way down - just using epoll/wantformultipleevents/select.

As near as I can tell, HttpClient is the newer MSFT .net API. Usually when MSFT comes up with new APIs for something they already support, they eventually deprecate/desupport the older API, so I would prefer HttpClient for that reason.

Whether or not connection caching helps you - depends on the nature of your application. It can help or hurt. If you make requests (using the same http client) to the same server, connection keepalives will help. If you do NOT do this, they will strictly HURT your performance.

4
  • What I mean by "all the way down", is that all code in the call chain supports async/await. If it doesn't then you end up blocking at some point and that defeats the purpose and can even lead to deadlocks. I don't mean "async all the way" in reference to the implementation of HttpClient.
    – Crowcoder
    Aug 12, 2018 at 14:38
  • @Crowcoder I dont know how HttpClient is implemented, but it can be implemented 'all the way down' using await. One way would be to have a separte thread that does the waitformultipleevents, and multiple threads doing async calls just hand off the sockets to this other thread and then do await, and the specail thread doing epoll/waitformultipleevents does the async wakeup when data available for the socket. Aug 12, 2018 at 14:45
  • I get the impression we are not talking about the same API. I'm talking about HttpClient from the System.Net namespace in c# .Net. Creating threads to do I/O bound work would be worse than blocking.
    – Crowcoder
    Aug 12, 2018 at 14:59
  • @Crowcoder I'm talking about the same API. Again, not claiming how its implemented (cuz I havent looked). But how it COULD be written. The async IO calls COULD push work into a queue, and then await on its competion. THen the work queue could be handled in another thread which gathers up all the IO handles and does a waitformultipleevents waiting for the IO to happen. That would be a sensible and obvious implementation, so its pretty likely its what MSFT did. Aug 12, 2018 at 15:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.