So at work recently, I’ve been looking into doing some much needed optimizations of our testing libraries. I wasn’t too familiar with the basics of how Python’s threading and multiprocessing machinery works (and I only had a vague idea of this global interpreter lock thing) so with nothing better to do on a Saturday night, I thought I’d spend a few hours digging in a little.

Some Prerequisite Concurrency Concepts

Before discussing Python’s concurrency model, we need to first cover a few general concurrency concepts.

Scheduling Classifications:

Non-preemptive Multitasking
Preemptive Multitasking

Threads, Fibers, Coroutines! Oh My!

~                Scheduling x Implementation level
                |  system-level  | language-level  |
|---------------|----------------------------------|
| preemptive    |  Threads       |    N/A          |
|---------------|----------------------------------|
| non-preemptive|  Fibers        |  Coroutines     |
|---------------|----------------------------------|

Threads

Fibers

Coroutines

Python Concurrency Concepts:

The GIL (Global Interpreter Lock)

Threads

Microthreads/Tasklets (actually just coroutines)

Greenlets (still just coroutines)

Multiprocessing

Conclusion

Python has a number of tools for concurrency, but I have yet to find any other solution to the GIL other than launching multiple python interpreters with the multiprocessing package. Which really where Python is beginning to show it’s age; utilizing multiple cores is becoming more and more valuable these days and ideally, our languages should make this easy and efficient. Python is wonderful for scripting, however it will continue to be limited to that realm until it gets a more elegant, non-cooperative, concurrency solution that isn’t just launching entirely new interpreters.

References: