You often see the statement, "you should never block". In other words if you need to make a call like "network_read()" that may take a signficant time to execute and return a result, it is bad to block.
This statement has to be evaluated in terms of "what thread will then block?". If we are talking about UI code and the thread in question is the thread that handles the main event loop, then yes -- it will be very bad to block and the entire application will "lock up" until the call that blocks has finished.
I argue however that in a properly designed system with multiple threads, most if not all threads should block! Consider some kind of system that needs to read some sensor and post data somewhere. Let's say that we want to read new data once per second. Then we should dedicate a thread for just that purpose and it should block over and over on some timer event. It will wake up on schedule, run briefly to read data and post it, then go back to sleep.
A second situation is what I call a "jobber thread". Imagine we have a one time need to do something that will take some time and block. In those cases we should launch a new thread to issue that request, block till it finishes, then notify us when it is done. Such a thread can then evaporate, or be returned to a pool of jobber threads for future such needs. This is just the sort of things that the C# async/await was invented to offer in a prefab way. Using await does exactly this -- it runs the await "thing" in a task newly minted for that purpose and allows the main flow of execution to continue without delay. The issue of synchronizing with that newly spawned task is "to be determined" by the caller, but in many cases no sychronization is needed. Just how the C# infrastructure finds a thread to run that new task is none of our concern -- and that is the big idea behind async/await.
Tom's Computer Info / tom@mmto.org