4/25/2025 at 9:10:22 AM
Very neat visualizations! Really good way to demonstrate the underlying principles. Great article.I will add one thing: the author makes much of how problematic CPU-intensive tasks are. That's true, but also: if you need to do a CPU-intensive operation, you need to tie up a CPU to do it, there's no getting around it. What you might want to do is break the computation up in to smaller chunks with `.await`s sprinkled in there or dispatch it to a background thread, but at some point you have to pay the cost in CPU time.
What is much more problematic is blocking I/O operations. That also ties up a CPU and blocks the event loop, but for no good reason. All the CPU is going to be doing is sleeping waiting for the network or hard-drives or whatever it is to do it's work, when it could be doing other useful things, processing other events. That kind of thing leads to huge under-utilization of your resources, and it kills your concurrency.
This means you really can't mix async I/O with blocking I/O: if you go async, ALL your I/O operations need to be async. Or you have to do it off the event loop in a background thread or something, but then you're losing all the benefits of using async in the first place.
by OskarS
4/25/2025 at 2:38:58 PM
I went through a phase of writing a lot of little things in async Python which was a lot of fun but I wrote an app that really tied up the event loop and now, at least for web servers, I develop with Flask, deploy to gunicorn and often front with IIS or nginx to serve up images and big files now.One thing I still like async Python for is programs that have a number of polling tasks going on at a slow cadence, say once a minute or less, where it doesn't really matter to me if a task gets delayed 5 seconds because of another task. In that case writing a number of tasks that await.sleep is easy and fun and works great -- I'll use async io in the tasks if it is easy to do so but if I use blocking I/O and it blocks... no problem.
At work I've been writing web servers in Java (or C#) for a couple of jobs and between threads and the garbage collector I also think it is easy and fun. Sure if I was handling Google-levels of traffic I might think differently, but threads scale pretty well for 99% of the web sites out there. Java gives you all the right primitives to do common tasks: sometimes I code something up with an Executor in 20 minutes and it works right the first time and gets a 14x speedup with 16 cores, somebody else tries Actors in the language of the day and spends a few days on it, never gets more than a 3x speedup and gets a different answer every time. (Threaded programming is easy when you have a rich primitive library you get in trouble when you get it in your head that it is noble to build everything in scratch from a small set of primitives)
Java even has virtual threads that play well with real threads if real threads aren't good enough for you.
by PaulHoule
4/25/2025 at 1:11:09 PM
> blocking I/O operations. That also ties up a CPU and blocks the event loopThey do block the event loop (if single-threaded) but they do not "tie up" a CPU (core), the OS will schedule other threads on it.
by remram
4/25/2025 at 1:54:43 PM
assuming you have other threadsbut in an async I/O server scenario, the typical architecture is a fixed-size pool of worker threads, which ideally entirely occupy a single core each. therefore, if one of those threads is blocked, that core is wasted.
by binary132