Some thoughts on asynchronous Python API design in a post-async/await world
The idea espoused in this blog post, that<p>&gt;  if you have N logical threads concurrently executing a routine with Y yield points, then there are N<i></i>Y possible execution orders that you have to hold in your head<p>is actively harmful to software maintainability. Concurrency problems don&#x27;t disappear when you make your yield points explicit.<p>Look: in traditional multi-threaded programs, we protect shared data using locks. If you avoid explicit locks and instead rely on complete knowledge of all yield points (i.e., all possible execution orders) to ensure that data races do not happen, then you&#x27;ve just created a ticking time-bomb: as soon as you add a new yield point, you invalidate your safety assumptions.<p>Traditional lock-based preemptive multi-threaded code isn&#x27;t susceptible to this problem: it already embeds maximally pessimistic assumptions about execution order, so adding a new preemption point cannot hurt anything.<p>Of course, you can use mutexes with explicit yield points too, but nobody does: the perception is that cooperative multitasking (or promises or whatever) frees you from having to worry about all that hard, nasty multi-threaded stuff you hated in your CS classes. But you haven&#x27;t really escaped. Those dining philosophers are still there, and now they&#x27;re <i>angry</i>.<p>The article claims that yield-based programming is easier because the fewer the total number of yield points, the less mental state a programmer needs to maintain. I don&#x27;t think this argument is correct: in lock-based programming, we need to keep _zero_ preemption points in mind, because we assume every instruction is a yield point. Instead of thinking about NY program interleavings, we think about how many locks we hold. I bet we have fewer locks than you have yields.<p>To put it another way, the composition properties of locks are much saner than the composition properties of safety-through-controlling-yield.<p>I believe that we got multithreaded programming basically right a long time ago, and that improvement now rests on approaches like reducing mutable shared state, automated thread-safety analysis, and software transactional memory. Encouraging developers to sprinkle &quot;async&quot; and &quot;await&quot; everywhere is a step backward in performance, readability, and robustness.