Fundamentals 19 min read

Python Multithreading: Concepts, Modules, and Practical Examples

This article explains Python multithreading fundamentals, covering thread basics, the _thread and threading modules, creating and managing threads, synchronization with locks, ThreadLocal usage, thread pools, and compares multithreading with multiprocessing for CPU‑bound and I/O‑bound tasks.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Python Multithreading: Concepts, Modules, and Practical Examples

Multithreading allows multiple threads to run concurrently, offering benefits such as background processing, responsive user interfaces, faster execution, and efficient resource utilization for I/O‑bound tasks.

Each thread has its own execution context, including registers like the instruction pointer and stack pointer, and can be either a kernel thread or a user thread.

Python provides two standard libraries for threading: _thread (low‑level) and threading (high‑level, recommended). The threading module includes useful functions such as threading.current_thread() , threading.enumerate() , and threading.active_count() , and the Thread class with methods like start() , run() , join() , is_alive() , getName() , and setName() .

Example of creating a simple thread with threading :

<code>import threading
import time

def print_numbers():
    for i in range(5):
        time.sleep(1)
        print(i)

thread = threading.Thread(target=print_numbers)
thread.start()
thread.join()
</code>

Creating threads using the low‑level _thread module:

<code>#!/usr/bin/python3
import _thread
import time

def print_time(threadName, delay):
    count = 0
    while count < 5:
        time.sleep(delay)
        count += 1
        print("%s: %s" % (threadName, time.ctime()))

try:
    _thread.start_new_thread(print_time, ("Thread-1", 2))
    _thread.start_new_thread(print_time, ("Thread-2", 4))
except:
    print("Error: unable to start thread")

while True:
    pass
</code>

Thread synchronization is achieved with Lock and RLock objects, using acquire() and release() to protect shared data.

<code>import threading
import time

class MyThread(threading.Thread):
    def __init__(self, threadID, name, delay):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.delay = delay
    def run(self):
        print("Starting thread:" + self.name)
        threadLock.acquire()
        print_time(self.name, self.delay, 3)
        threadLock.release()

def print_time(threadName, delay, counter):
    while counter:
        time.sleep(delay)
        print("%s: %s" % (threadName, time.ctime()))
        counter -= 1

threadLock = threading.Lock()
thread1 = MyThread(1, "Thread-1", 1)
thread2 = MyThread(2, "Thread-2", 2)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print("Main thread exiting")
</code>

Thread‑local storage ( threading.local() ) provides each thread with its own independent variables, useful for per‑thread database connections or request contexts.

<code>import threading
local_school = threading.local()

def process_student():
    print('Hello, %s (in %s)' % (local_school.student, threading.current_thread().name))

def process_thread(name):
    local_school.student = name
    process_student()

t1 = threading.Thread(target=process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target=process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
</code>

Thread pools can be created using multiprocessing.dummy.Pool for easy management of multiple worker threads.

Comparing multithreading and multiprocessing: processes offer higher stability (a crash in one process does not affect others) but have higher creation overhead, while threads share memory and can cause the entire process to crash if one thread fails.

Task types affect concurrency strategy: CPU‑bound tasks should match the number of CPU cores, whereas I/O‑bound tasks benefit from many more threads to hide latency.

Asynchronous I/O and event‑driven models (e.g., using coroutines) allow single‑process programs to handle many I/O‑bound tasks efficiently, reducing the need for multiple threads.

PythonconcurrencythreadpoolSynchronizationmultithreadingthreading
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

login 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.