|
Those are all fair points! They apply more or less equally to, say, nodejs or Perl's AnyEvent. I'm totally onboard with "use the OS thread scheduler when you can"; it's a brilliant piece of technology. And if you are in a situation where > pauses [are] an order of magnitude longer than the threaded version's median response time ...then fully cooperative, single-thread async programming isn't for you! But async programming was made for the inverse use-case of that: backend functions which spend the vast majority of their time waiting for I/O--slow databases, HTTP requests, and whatnot--and which needed to serve very high concurrency demands on very cheap servers. If, say, your web route handler wants to probe or parallel-fetch from 100 unique-per-request HTTP endpoints, and you expect to handle 100 web requests concurrency, using threads is going to cost a lot of resources (the portion stack space that's allocated eagerly, start/stop time, and so on). For some folks, those resources might be readily available, in which case threads may still be the way to go! But for other folks (tiny servers, or more orders of magnitude than 100/100--use cases like proxies or CDNs) cooperative async might be a better way to go. Similarly, if your concurrent code is sharing a lot of data between routines but is primarily waiting on I/O, single-thread async concurrency might be a less race-condition-bug-prone model to use. I'm generally with you that it's a tool that's prioritized above threads in a lot of cases when it shouldn't be. But it is still definitely valuable for a lot of common challenges. A lot of the above is rehashing the comments I made here: https://news.ycombinator.com/item?id=39247876 |
Even if you buy into callback-style async style programming like node or python async, Python async is not just bad because the general idea of it is bad, it is also bad-at-what-it-is-trying-to-be. It's a bad, hack, incompetent implementation of async programming, which in this style (cooperative callback style) is a bad architecture. It's unforgivably bad. Look in the stdlib and read the code for it, it's full of gotchas and confusion and patch after patch after patch putting in exceptions (like the "we want to do W but if this task is x then actually don't do this but if it's y do this other thing and if it's z then do a third thing kind of exception) and checks and double checks every 3rd line because the design itself is broken. Whole features (like un-cancel task) were put in because one influential person demanded it because the library they wrote can't work without it, even though it breaks a fundamental invariant that should never be violated.
Fundamentally, when you choose a tool or architecture, it should not be the thing you spend 50% of your time debugging. Every Python async project I've ever worked on required me to spend half my time tracing around in the guts of the async library itself to figure out what weird edge case we were running into that made something completely broken. This is like the hallmark of bad software.