strboul blog

Multithreading in Python


Python threads are useful for isolating programs and performing background tasks.

import threading
import subprocess
import time

def calculate_sum(sleep, which_thread):
    time.sleep(sleep)
    result = 2 + 2
    print(f"{which_thread}: 2 + 2 = {result}")

def sleep(time, which_thread):
    process = subprocess.Popen(
        ["sleep", str(time)], stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )
    exit_code = process.wait()
    status = "finished" if exit_code == 0 else "failed"
    print(f"{which_thread}: sleep '{time}' {status}. exit code: {exit_code}")

if __name__ == "__main__":
    threads = [
        threading.Thread(target=calculate_sum, args=(0, "Thread 1",)),
        threading.Thread(target=calculate_sum, args=(1, "Thread 2",)),
        threading.Thread(target=sleep, args=(5, "Thread 3",)),
        threading.Thread(target=sleep, args=("fail", "Thread 4",)),
    ]
    # Start threads
    for thread in threads:
        thread.start()
    # Wait for both threads to finish
    for thread in threads:
        thread.join()
    # The program will continue once both threads have finished
    print("Program completed.")

# Thread 1: 2 + 2 = 4
# Thread 4: sleep 'fail' failed. exit code: 1
# Thread 2: 2 + 2 = 4
# Thread 3: sleep '5' finished. exit code: 0
# Program completed.

Yet, threading in Python doesn't always inherently enhance performance. CPU-intensive executions may not fully exploit threading due to the GIL constraint but I/O-bound operations can still get some degree of benefit. There's some work on CPython that GIL may be removed in the future versions of Python (currently 3.12+ is the latest one).