Interview Q&A / Python

AdvancedIn-depth5 min readUpdated 2 Oct 2026

What is the GIL in Python, and when should you use threads versus multiprocessing?

SHORT ANSWER

The Global Interpreter Lock in CPython lets only one thread run Python bytecode at a time, so threads do not speed up CPU-heavy pure Python code. Threads still work well for I/O-bound work, because the lock is released while waiting. For CPU-bound work, use multiprocessing. Python 3.13 added an optional, experimental free-threaded build without the GIL.

Explain it simply

Picture a college library with one shared computer for issuing books, and four librarians. Only one librarian can use the computer at a time. If a librarian walks to the shelves to fetch a book, she leaves the computer free and another librarian can use it. So when most of the work is walking around, four librarians help a lot. But if all the work is typing on that one computer, adding more librarians does not make it faster.

The GIL (Global Interpreter Lock) is that one computer. It is a lock inside CPython, the standard Python interpreter. A thread must hold it to run Python code.

  • I/O-bound work (waiting for a network reply, a database or a file) is like walking to the shelves. Threads release the GIL while they wait, so threads help.
  • CPU-bound work (heavy calculations in pure Python) is like typing. Only one thread can do it at a time, so threads do not help.

Multiprocessing is like opening a second library branch with its own computer. Each process has its own interpreter and its own GIL, so they can truly run on different CPU cores at once.

Example

python
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import urllib.request

def fetch(url):                       # I/O-bound: mostly waiting
    with urllib.request.urlopen(url) as r:
        return len(r.read())

def count_primes(limit):              # CPU-bound: pure computation
    count = 0
    for n in range(2, limit):
        if all(n % d for d in range(2, int(n ** 0.5) + 1)):
            count += 1
    return count

if __name__ == "__main__":
    urls = ["https://example.com"] * 10
    with ThreadPoolExecutor(max_workers=10) as pool:      # threads for I/O
        sizes = list(pool.map(fetch, urls))

    limits = [200_000] * 4
    with ProcessPoolExecutor() as pool:                   # processes for CPU
        results = list(pool.map(count_primes, limits))

The if __name__ == "__main__": guard matters for multiprocessing on Windows and macOS, where child processes re-import the main module.

How it works

  • CPython manages memory with reference counting. The GIL keeps those counts and other interpreter internals safe without putting a lock on every object, which keeps single-threaded code simple and fast.
  • A running thread is asked to give up the GIL at regular intervals so others get a turn, and it releases the GIL during blocking I/O.
  • Many C extensions, such as NumPy for large array operations, release the GIL while doing heavy work in C, so threads can help there too.
  • Processes do not share memory by default. Data passed between them is pickled and copied, which has a cost. Starting a process is also slower than starting a thread.
  • The GIL does not make your own code thread-safe. Operations like count += 1 are several steps, and threads can still interleave between them. Use threading.Lock for shared state.

Real-world use

  • Threads or asyncio: a scraper fetching 500 product pages, a service calling several payment or SMS APIs, downloading files.
  • Multiprocessing: resizing thousands of images, parsing large CSVs, running a simulation, CPU-heavy feature engineering.
  • Neither: for heavy number crunching, libraries like NumPy and pandas already run the loops in optimised C code.

Common mistakes

  • Saying "Python cannot do multithreading". It can; threads just do not run Python bytecode in parallel in standard CPython.
  • Using threads for CPU-heavy pure Python and expecting a speed-up.
  • Assuming the GIL removes the need for locks.
  • Starting hundreds of processes. Use a pool sized near the number of CPU cores.
  • Saying the GIL is part of the Python language. It is a detail of CPython; the language specification does not require it.

What to say in the interview

"The GIL is a lock in CPython that allows only one thread to execute Python bytecode at a time. It keeps memory management simple, but it means threads do not give parallel speed-up for CPU-bound pure Python code. Threads are still useful for I/O-bound work like network calls, because the GIL is released while a thread waits. For CPU-bound tasks I use multiprocessing or ProcessPoolExecutor, since each process has its own interpreter and GIL and can use a separate core. I would also mention that Python 3.13 introduced an optional free-threaded build where the GIL can be disabled. It is a separate build, not the default, and it started as experimental."

Follow-up questions

  • Why do threads help when downloading files but not when computing primes?
  • How is asyncio different from threading?
  • Does the GIL make a list.append from two threads safe? Does it make count += 1 safe?
  • How do processes share data with each other?

Go deeper

Read these once the basics above feel easy.

asyncio runs many tasks on a single thread. Tasks give up control at each await, usually while waiting on I/O. It handles thousands of connections with less overhead than thousands of threads, but every library call in the path must be async-aware, and one blocking call stalls everything.

To share data between processes, use multiprocessing.Queue or Pipe for messages, Value and Array or shared_memory for shared buffers, or simply return results from a pool and combine them in the parent.

#python#gil#concurrency#threads#multiprocessing

Related questions

Practise more Python questions

Test your recall with short answers, then return to the detailed explanations when you need context.

Open Python quick answers →

Get new questions like this by email

One short email when something new and useful lands. No spam, unsubscribe in one tap.

By subscribing you agree to the Privacy Policy.