Asynchronous programming

Marko Rauhamaa marko at pacujo.net
Thu Aug 11 03:55:41 EDT 2016


Chris Angelico <rosuav at gmail.com>:

> Hmm. I'm not sure about that last bit. In order to properly support
> asynchronous I/O and the concurrency and reentrancy that that implies,
> you basically need all the same disciplines that you would for
> threaded programming

Correct. Python's asyncio closely mirrors threads in its API and thought
model. An async is like a thread except that you know the control will
only be relinquished at an "await" statement. Thus, you probably need
far less locking.

One visible difference between asyncs and threads is the fact that
threads don't need a special "await" construct. Instead, ordinary
function calls support context switching deep down in the Python VM and
the operating system. Each "await" explicitly marks a state in a state
machine. Whenever you turn a nonblocking function into a blocking one,
you will need to change the function call to an await statement.

Asyncs have a couple of distinct advantages over threads:

 * You can await multiple events in a single statement allowing for
   multiplexing. Threads can only wait for a single event.

 * Asyncs can be canceled; in general, threads can't.

> But maybe I'm too comfortable with threads. It's entirely possible; my
> first taste of reentrancy was interrupt handling in real mode 80x86
> assembly language, and in comparison to that, threads are pretty
> benign!

I steer clear of threads if I can because:

 * No multiplexing.

 * No cancellation.

 * Deadlocks and/or race conditions are virtually guaranteed.

 * Error symptoms are highly nonlocal making debugging very tricky.

Instead, I strongly prefer asynchronous programming and multiprocessing.

However, I'm afraid Python is following an unfortunate trend with
asyncio. Deep down concurrent programming involves finite state
machines. State machines can be expressed in many ways; traditionally,
you have SDL diagrams, state/event matrices etc. Asyncio marks states
with inline "await" keywords. Toy examples look really enticing and
readable compared with the more traditional implementation methods.
However, real-world code can quickly become spaghetti as the dozens of
cells of the state/event matrix are implemented.

My favorite asynchronous development model is the "callback hell," where
each cell of the state/event matrix is represented by a method (or at
least a switch case in C) and each state has an explicit name in the
source code. The resulting code is still hard to read and hard to get
right, but that complexity is unavoidable because reality just is that
complex.


Marko



More information about the Python-list mailing list