The global interpreter lock (GIL) limits CPython to one thread executing Python code at a time. OS threads can interleave, but CPU-bound Python threads do not execute in parallel across cores. Concurrency means tasks overlap in time; parallelism means they run at the same time. The distinction matters when choosing how to speed up data processing. section 1.3.1 section 3.3.3
Futures are not proof of parallelism
Submitting mapper calls to a ThreadPoolExecutor returns futures that can be checked while work is pending. This makes a MapReduce pipeline concurrent, but the GIL prevents its Python mapper threads from running in parallel. A mapper that sleeps illustrates why overlap can still be useful when tasks spend time waiting. section 3.3.2
from collections import defaultdict
from concurrent.futures import ThreadPoolExecutor as Executor
def async_map(executor, mapper, data):
futures = []
for datum in data:
futures.append(executor.submit(mapper, datum)) #1
return futures
def map_less_naive(executor, my_input, mapper):
map_results = async_map(executor, mapper, my_input)
return map_results
The thread-pool mapper submits each datum and returns futures rather than completed results. That change enables asynchronous progress; it does not, by itself, remove the GIL’s limit on Python execution. section 3.3.2
The GIL is not necessarily the dominant performance cost. Multicore thread switching can add friction, while high-performance inner loops often belong in lower-level code. Threading can still serve I/O-heavy work or program structure; it is rarely the best route to faster CPU-bound Python code. section 1.3.1 section 3.3.3 section 3.6
Choose an escape route according to where the expensive work runs:
- Use multiprocessing for parallel pure-Python computation. Keep tasks coarse enough that communication overhead does not erase the gain. section 3.4 section 3.6
- Move the critical loop into lower-level code that can release the GIL. NumPy, SciPy, and scikit-learn can run parallel work in their C or Fortran implementations. section 3.3.3
- For custom array work, reduce CPython interactions in Cython before attempting GIL-independent threaded execution. A color Game of Life implementation provides a concrete array-processing target. section 5.6 section 5.8




