Python Practice Question 8: GIL CPU Bound Parallelism
Python · Test 1 · question 8 of 10
A service scores images with a pure-Python, CPU-bound function (no I/O, no C extensions). It runs on an 8-core machine using the standard CPython 3.12 build. Moving the work from a plain loop to a ThreadPoolExecutor with 8 workers produced no speedup, and one CPU core sits at 100% while the others stay idle.
Which change is most likely to make the scoring use all 8 cores?
Show answer and explanation
Answer:
The standard CPython build has a Global Interpreter Lock (GIL): only one thread executes Python bytecode at a time. Threads still help with I/O-bound work, because the GIL is released while a thread waits, but pure-Python CPU-bound work is effectively serialized, which matches the one busy core. ProcessPoolExecutor runs work in separate worker processes, each with its own interpreter and its own GIL, so the tasks run in parallel on separate cores. It uses the same concurrent.futures interface, so the change is small (the function and its arguments must be picklable).
Option A adds more threads that still share one GIL; you get more contention and no parallelism.
Option B is also single-threaded. asyncio interleaves tasks at await points and is useful for I/O concurrency; a CPU-bound coroutine never yields, so it runs the batch one item at a time on one core.
Option D changes only how often the running thread is asked to release the GIL. The GIL still passes between threads; it never allows two to run bytecode at once.
Version note: CPython 3.13 added an optional free-threaded build (no GIL), first as an experimental build. The stem specifies the standard 3.12 build, which always has the GIL.
References: Glossary: global interpreter lock; concurrent.futures: ProcessPoolExecutor