threading 是 Python 中用于实现多线程编程的标准库模块,它建立在 _thread 模块之上,提供了更高级的线程管理接口。

基本概念

线程 vs 进程

  • 进程:操作系统分配资源的基本单位,每个进程有独立的内存空间
  • 线程:进程内的执行单元,共享进程的内存空间,创建和切换开销更小

GIL(全局解释器锁)

Python 的 GIL 限制了同一时刻只能有一个线程执行 Python 字节码,因此:

  • I/O 密集型任务:多线程能有效提高效率
  • CPU 密集型任务:多线程可能无法提高性能,应考虑多进程

基本用法

创建线程

import threading
import time

# 方法1:继承 Thread 类
class MyThread(threading.Thread):
    def __init__(self, name):
        super().__init__()
        self.name = name
        
    def run(self):
        print(f"线程 {self.name} 开始")
        time.sleep(2)
        print(f"线程 {self.name} 结束")

# 方法2:使用函数
def worker(name):
    print(f"线程 {name} 开始")
    time.sleep(2)
    print(f"线程 {name} 结束")

# 创建线程
thread1 = MyThread("Thread-1")
thread2 = threading.Thread(target=worker, args=("Thread-2",))

# 启动线程
thread1.start()
thread2.start()

# 等待线程结束
thread1.join()
thread2.join()

print("所有线程执行完毕")

线程同步

1. Lock(锁)

import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    for _ in range(100000):
        with lock:  # 自动获取和释放锁
            counter += 1

threads = []
for i in range(5):
    thread = threading.Thread(target=increment)
    threads.append(thread)
    thread.start()

for thread in threads:
    thread.join()

print(f"最终计数器值: {counter}")  # 应该是 500000

2. RLock(可重入锁)

# RLock 允许同一个线程多次获取锁
rlock = threading.RLock()

def recursive_function(count):
    with rlock:
        if count > 0:
            print(f"获取锁,计数: {count}")
            recursive_function(count - 1)

thread = threading.Thread(target=recursive_function, args=(5,))
thread.start()
thread.join()

3. Condition(条件变量)

import threading
import time

# 生产者-消费者示例
items = []
condition = threading.Condition()

def consumer():
    with condition:
        while not items:
            print("消费者等待...")
            condition.wait()  # 释放锁并等待
        item = items.pop()
        print(f"消费: {item}")

def producer():
    with condition:
        items.append("产品")
        print("生产了一个产品")
        condition.notify()  # 通知等待的消费者

# 创建线程
consumer_thread = threading.Thread(target=consumer)
producer_thread = threading.Thread(target=producer)

consumer_thread.start()
time.sleep(1)  # 确保消费者先等待
producer_thread.start()

consumer_thread.join()
producer_thread.join()

4. Semaphore(信号量)

# 限制同时访问资源的线程数量
semaphore = threading.Semaphore(3)  # 最多3个线程同时访问

def access_resource(thread_id):
    with semaphore:
        print(f"线程 {thread_id} 访问资源")
        time.sleep(2)
        print(f"线程 {thread_id} 释放资源")

threads = []
for i in range(10):
    thread = threading.Thread(target=access_resource, args=(i,))
    threads.append(thread)
    thread.start()

for thread in threads:
    thread.join()

5. Event(事件)

# 线程间通信的简单机制
event = threading.Event()

def waiter():
    print("等待事件发生...")
    event.wait()  # 阻塞直到事件被设置
    print("事件已发生,继续执行")

def setter():
    time.sleep(2)
    print("设置事件")
    event.set()  # 设置事件,唤醒所有等待的线程

t1 = threading.Thread(target=waiter)
t2 = threading.Thread(target=setter)

t1.start()
t2.start()

t1.join()
t2.join()

线程局部数据

# 每个线程有自己独立的数据副本
local_data = threading.local()

def show_data():
    try:
        print(f"线程 {threading.current_thread().name}: {local_data.value}")
    except AttributeError:
        print(f"线程 {threading.current_thread().name}: 没有数据")

def set_data(value):
    local_data.value = value
    show_data()

thread1 = threading.Thread(target=set_data, args=("线程1的数据",), name="Thread-1")
thread2 = threading.Thread(target=set_data, args=("线程2的数据",), name="Thread-2")

thread1.start()
thread2.start()

thread1.join()
thread2.join()

定时器线程

def hello():
    print("Hello, world!")

# 5秒后执行hello函数
timer = threading.Timer(5.0, hello)
timer.start()

print("定时器已启动,等待5秒...")

线程池(Python 3.2+)

from concurrent.futures import ThreadPoolExecutor
import time

def task(name, duration):
    print(f"任务 {name} 开始")
    time.sleep(duration)
    print(f"任务 {name} 完成")
    return f"任务 {name} 结果"

# 使用线程池
with ThreadPoolExecutor(max_workers=3) as executor:
    # 提交任务
    future1 = executor.submit(task, "A", 2)
    future2 = executor.submit(task, "B", 1)
    future3 = executor.submit(task, "C", 3)
    
    # 获取结果
    print(future1.result())
    print(future2.result())
    print(future3.result())

守护线程

def daemon_worker():
    while True:
        print("守护线程运行中...")
        time.sleep(1)

def normal_worker():
    print("普通线程开始")
    time.sleep(3)
    print("普通线程结束")

# 创建守护线程
daemon_thread = threading.Thread(target=daemon_worker)
daemon_thread.daemon = True  # 设置为守护线程
daemon_thread.start()

# 创建普通线程
normal_thread = threading.Thread(target=normal_worker)
normal_thread.start()

normal_thread.join()
print("主程序结束")  # 守护线程会随主程序结束而终止

最佳实践

  1. 避免使用全局变量:使用线程局部数据或传递参数
  2. 合理使用锁:尽量减少锁的持有时间
  3. 使用线程安全的数据结构:如 queue.Queue
  4. 考虑使用线程池:避免频繁创建销毁线程的开销
  5. 处理异常:线程中的异常不会传播到主线程
# 线程异常处理示例
def safe_worker():
    try:
        # 可能出错的代码
        raise ValueError("线程内部错误")
    except Exception as e:
        print(f"线程捕获到异常: {e}")

thread = threading.Thread(target=safe_worker)
thread.start()
thread.join()

threading 模块为 Python 提供了强大的多线程编程能力,但在使用时需要注意线程安全和 GIL 的限制。