Fundamentals 38 min read

Understanding Python Threads, Processes, GIL, and Multiprocessing

This article explains the fundamental differences between threads and processes, the role of Python's Global Interpreter Lock (GIL), and how to use the multiprocessing package—including Process, Pool, Queue, Pipe, and synchronization primitives—as well as an overview of concurrent.futures for high‑level concurrent programming in Python.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Understanding Python Threads, Processes, GIL, and Multiprocessing

Thread and Process Differences

In operating systems, a process is the smallest unit of resource allocation, while a thread is the smallest unit of CPU scheduling; threads share a process's address space and resources, making context switches cheaper and improving concurrency.

Key distinctions include address‑space isolation, communication methods (IPC for processes, shared memory for threads), scheduling overhead, and reliability (a crash in one thread can bring down the whole process).

Python Global Interpreter Lock (GIL)

The GIL ensures that only one thread executes Python bytecode at a time in CPython, simplifying the interpreter but limiting true parallelism on multi‑core CPUs. It benefits single‑threaded performance and C‑extension integration, but causes thread thrashing on CPU‑bound workloads.

Since Python 3.2 the GIL is released after a fixed timeout (≈5 ms) when other threads request it, improving fairness on multi‑core systems.

Mitigating GIL Impact

Upgrade to newer Python versions with optimized GIL handling.

Use multiprocessing instead of multithreading for CPU‑bound tasks.

Pin threads to specific CPUs (affinity).

Employ GIL‑free interpreters such as Jython or IronPython.

Prefer I/O‑bound workloads for threading.

Use coroutines (asyncio) for efficient single‑threaded concurrency.

Write performance‑critical sections in C/C++ extensions (with nogil).

Python Multiprocessing Package

The multiprocessing module provides a process‑based parallelism API that mirrors the threading interface, allowing easy migration from threads to processes. Each process has its own GIL, eliminating GIL contention.

Process

Creates a new process.

from multiprocessing import Process
import os

def run(name):
    print('Run child process %s (%s)...' % (name, os.getpid()))

if __name__ == '__main__':
    print('Parent process %s.' % os.getpid())
    p = Process(target=run, args=('test',))
    p.start()
    p.join()
    print('Child process end.')

Pool

Manages a pool of worker processes, reusing them for multiple tasks.

from multiprocessing import Pool

def test(i):
    print(i)

if __name__ == '__main__':
    pool = Pool(8)
    pool.map(test, range(100))
    pool.close()
    pool.join()

Queue, JoinableQueue

Provides inter‑process communication via FIFO queues.

from multiprocessing import Process, Queue
import os, time, random

def write(q):
    for v in ['A','B','C']:
        q.put(v)
        time.sleep(random.random())

def read(q):
    while True:
        v = q.get(True)
        print('Got', v)

if __name__ == '__main__':
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    pw.start(); pr.start()
    pw.join(); pr.terminate()

Value and Array

Shared memory objects for simple data types.

from multiprocessing import Value, Array, Process

def f(num, arr):
    num.value = 3.14
    arr[0] = 5

if __name__ == '__main__':
    num = Value('d', 0.0)
    arr = Array('i', range(10))
    p = Process(target=f, args=(num, arr))
    p.start(); p.join()
    print(num.value, arr[:])

Pipe

Creates a two‑way communication channel.

from multiprocessing import Process, Pipe
import time

def child(conn):
    time.sleep(1)
    conn.send('Hello')
    print('Child received', conn.recv())
    conn.close()

if __name__ == '__main__':
    parent, child_conn = Pipe()
    p = Process(target=child, args=(child_conn,))
    p.start()
    print('Parent received', parent.recv())
    parent.send('OK')
    p.join()

Manager

Runs a server process that hosts shared objects (list, dict, Namespace, etc.) accessible via proxies.

import multiprocessing as mp

def f(x, arr, lst, dct, ns):
    x.value = 3.14
    arr[0] = 5
    lst.append('Hello')
    dct[1] = 2
    ns.a = 10

if __name__ == '__main__':
    mgr = mp.Manager()
    x = mgr.Value('d', 0.0)
    arr = mgr.Array('i', range(10))
    lst = mgr.list()
    dct = mgr.dict()
    ns = mgr.Namespace()
    p = mp.Process(target=f, args=(x, arr, lst, dct, ns))
    p.start(); p.join()
    print(x.value, arr[:], lst, dct, ns)

Synchronization Primitives

Lock : Simple mutex.

RLock : Re‑entrant lock.

Semaphore : Allows a limited number of concurrent accesses.

Condition : Advanced lock with wait/notify semantics.

Event : Simple flag for signaling between processes.

Example: Using Lock

from multiprocessing import Process, Lock

def worker(l, n):
    l.acquire()
    print('Hello Num:', n)
    l.release()

if __name__ == '__main__':
    lock = Lock()
    for i in range(20):
        Process(target=worker, args=(lock, i)).start()

concurrent.futures

High‑level API introduced in Python 3.2 that provides ThreadPoolExecutor and ProcessPoolExecutor for easy asynchronous execution.

Submitting Tasks

from concurrent.futures import ThreadPoolExecutor
import time

def test(num):
    return time.ctime(), num

with ThreadPoolExecutor(max_workers=1) as exe:
    fut = exe.submit(test, 1)
    print(fut.result())

Mapping Over Iterables

from concurrent.futures import ThreadPoolExecutor

def test(num):
    return time.ctime(), num

with ThreadPoolExecutor(max_workers=1) as exe:
    for result in exe.map(test, [1,2,3]):
        print(result)

Future API

Futures expose methods such as result(), exception(), cancel(), and utilities like as_completed() and wait() for coordinating multiple asynchronous operations.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonconcurrencythreadingGILmultiprocessing
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.