Python Multithreading Basics

Example 1: Basic Thread Creation

import threading
import time

def func01(number):
    print("Function func01 start")
    time.sleep(2)
    print("Function func01 end")

def func02(number):
    print("Function func02 start")
    time.sleep(2)
    print("Function func02 end")

if __name__ == '__main__':
    thread1 = threading.Thread(target=func01, args=(1,))
    thread2 = threading.Thread(target=func02, args=(2,))

    start_time = time.time()
    thread1.start()
    thread2.start()
    end_time = time.time()

    print("Time elapsed:", end_time - start_time)

Daemon Threads

If a thread must run in an infinite loop and should not prevent the program from exiting, set it as a daemon thread. When only daemon threads remain, the Python program can exit gracefully.

Purpose: Daemon threads provide backrgound services, like the garbage collector.

Notes:

  1. Daemon threads terminate once all non-daemon threads have completed.
  2. The main thread finishes when all non-daemon thread are done, at which point daemon threads are reclaimed.

By default, child threads are non-daemon. Setting a thread as daemon allows the program to exit without waiting for it.

import threading
import time

def func01(number):
    print("Function func01 start")
    time.sleep(2)
    print("Function func01 end")

def func02(number):
    print("Function func02 start")
    time.sleep(4)  # Longer sleep to distinguish
    print("Function func02 end")

if __name__ == '__main__':
    thread1 = threading.Thread(target=func01, args=(1,))
    thread2 = threading.Thread(target=func02, args=(2,))

    start_time = time.time()
    thread2.setDaemon(True)  # Set daemon before start
    thread1.start()
    thread2.start()
    end_time = time.time()

    print("Time elapsed:", end_time - start_time)

Joining Threads (Blocking Main Thread)

Use join() to make the main thread wait for child threads to complete.

import threading
import time

def func01(number):
    print("Function func01 start")
    time.sleep(2)
    print("Function func01 end")

def func02(number):
    print("Function func02 start")
    time.sleep(4)
    print("Function func02 end")

if __name__ == '__main__':
    thread1 = threading.Thread(target=func01, args=(1,))
    thread2 = threading.Thread(target=func02, args=(2,))

    start_time = time.time()
    thread2.setDaemon(True)
    thread1.start()
    thread2.start()

    thread1.join()
    thread2.join()

    end_time = time.time()
    print("Time elapsed:", end_time - start_time)

Thread Management with Classes

Inherit from threading.Thread and override the run method.

import threading
import time

class Student1(threading.Thread):
    def __init__(self, name):
        super().__init__(name=name)

    def run(self):
        print("Student1 start")
        time.sleep(2)
        print("Student1 end")

class Student2(threading.Thread):
    def __init__(self, name):
        super().__init__(name=name)

    def run(self):
        print("Student2 start")
        time.sleep(2)
        print("Student2 end")

if __name__ == '__main__':
    thread1 = Student1("Alice")
    thread2 = Student2("Bob")

    start_time = time.time()
    thread2.setDaemon(True)
    thread1.start()
    thread2.start()

    thread1.join()
    thread2.join()

    end_time = time.time()
    print("Time elapsed:", end_time - start_time)

Inter-thread Communication

Using Shared Variables (Not Thread-safe)

import threading
import time

url_list = []

def fetch_detail(url_list):
    while True:
        if url_list:
            url = url_list.pop()
            print("Fetch detail start")
            time.sleep(2)
            print("Fetch detail end")

def fetch_list(url_list):
    while True:
        print("Fetch list start")
        time.sleep(4)
        for i in range(20):
            url_list.append(f"http://example.com/{i}")
        print("Fetch list end")

if __name__ == '__main__':
    start_time = time.time()
    list_thread = threading.Thread(target=fetch_list, args=(url_list,))
    for _ in range(5):
        detail_thread = threading.Thread(target=fetch_detail, args=(url_list,))
        detail_thread.start()
    list_thread.start()

    end_time = time.time()
    print("Time elapsed:", end_time - start_time)

Using Queue (Thread-safe)

import threading
import time
from queue import Queue

def fetch_detail(queue):
    while True:
        url = queue.get()  # Blocks if queue is empty
        print("Fetch detail start")
        time.sleep(2)
        print("Fetch detail end")

def fetch_list(queue):
    while True:
        print("Fetch list start")
        time.sleep(4)
        for i in range(20):
            queue.put(f"http://example.com/{i}")
        print("Fetch list end")

if __name__ == '__main__':
    start_time = time.time()
    url_queue = Queue(maxsize=1000)
    list_thread = threading.Thread(target=fetch_list, args=(url_queue,))
    for _ in range(5):
        detail_thread = threading.Thread(target=fetch_detail, args=(url_queue,))
        detail_thread.start()
    list_thread.start()

    url_queue.join()
    end_time = time.time()
    print("Time elapsed:", end_time - start_time)

Thread Synchronization

Using Lock

from threading import Lock, Thread

total = 0
lock = Lock()

def add():
    global total
    for _ in range(1000):
        lock.acquire()
        total += 1
        lock.release()

def subtract():
    global total
    for _ in range(1000):
        lock.acquire()
        total -= 1
        lock.release()

thread1 = Thread(target=add)
thread2 = Thread(target=subtract)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(total)

Using RLock (Reentrant Lock)

from threading import RLock, Thread

total = 0
lock = RLock()

def add():
    global total
    for _ in range(1000):
        lock.acquire()
        lock.acquire()  # Nested acquire with RLock
        total += 1
        lock.release()
        lock.release()

def subtract():
    global total
    for _ in range(1000):
        lock.acquire()
        total -= 1
        lock.release()

thread1 = Thread(target=add)
thread2 = Thread(target=subtract)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(total)

Using Condition (Coordination)

import threading

class XiaoAi(threading.Thread):
    def __init__(self, cond):
        super().__init__(name="XiaoAi")
        self.cond = cond

    def run(self):
        with self.cond:
            self.cond.wait()
            print(f"{self.name}: Here")
            self.cond.notify()

            self.cond.wait()
            print(f"{self.name}: OK")
            self.cond.notify()

            self.cond.wait()
            print(f"{self.name}: You live at the Yangtze River's end")
            self.cond.notify()

            self.cond.wait()
            print(f"{self.name}: We both drink the same river water")
            self.cond.notify()

            self.cond.wait()
            print(f"{self.name}: When will this sorrow end?")
            self.cond.notify()

            self.cond.wait()
            print(f"{self.name}: I will not fail your love")
            self.cond.notify()

class TianMao(threading.Thread):
    def __init__(self, cond):
        super().__init__(name="TianMao")
        self.cond = cond

    def run(self):
        with self.cond:
            print(f"{self.name}: XiaoAi?")
            self.cond.notify()
            self.cond.wait()

            print(f"{self.name}: Let's recite a poem")
            self.cond.notify()
            self.cond.wait()

            print(f"{self.name}: I live at the Yangtze River's head")
            self.cond.notify()
            self.cond.wait()

            print(f"{self.name}: I miss you every day but cannot see you")
            self.cond.notify()
            self.cond.wait()

            print(f"{self.name}: When will this river cease?")
            self.cond.notify()
            self.cond.wait()

            print(f"{self.name}: I desire your heart like mine")
            self.cond.notify()
            self.cond.wait()

            print(f"{self.name}: Then I will not fail your affection")
            self.cond.notify()
            self.cond.wait()

if __name__ == '__main__':
    cond = threading.Condition()
    xiaoai = XiaoAi(cond)
    tianmao = TianMao(cond)

    # Start order matters
    xiaoai.start()
    tianmao.start()

Using Semaphore (Controlling Concurrency)

import threading
import time

class HtmlSpider(threading.Thread):
    def __init__(self, url, sem):
        super().__init__()
        self.url = url
        self.sem = sem

    def run(self):
        time.sleep(2)
        print("Got HTML text success")
        self.sem.release()

class UrlProducer(threading.Thread):
    def __init__(self, sem):
        super().__init__()
        self.sem = sem

    def run(self):
        for i in range(20):
            self.sem.acquire()
            spider = HtmlSpider(f"http://example.com/{i}", self.sem)
            spider.start()

if __name__ == '__main__':
    sem = threading.Semaphore(3)  # Limit concurrent threads to 3
    producer = UrlProducer(sem)
    producer.start()

Tags: python multithreading threading Concurrency

Posted on Fri, 22 May 2026 20:08:33 +0000 by mark110384