RTFN

Python 多线程编程

Python 提供了threading模块用于多线程编程,但其实现有一些独特之处。

基本概念与用法

创建和启动线程

import threading
import time

def worker(name):
    print(f"线程 {name} 开始工作")
    time.sleep(2)  # 模拟工作
    print(f"线程 {name} 完成工作")

# 创建多个线程
threads = []
for i in range(5):
    t = threading.Thread(target=worker, args=(f"worker-{i}",))
    threads.append(t)
    t.start()  # 启动线程

# 等待所有线程完成
for t in threads:
    t.join()

print("所有线程工作完成")

使用线程类

class WorkerThread(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} 完成工作")

# 创建并启动线程
for i in range(3):
    thread = WorkerThread(f"custom-{i}")
    thread.start()

全局解释器锁 (GIL)

Python 多线程的最大限制是 GIL (Global Interpreter Lock):

# I/O密集型任务 - 多线程有明显优势
def io_bound_task():
    # 读取文件、网络请求等操作
    time.sleep(1)  # 模拟I/O等待

# CPU密集型任务 - 多线程可能没有优势
def cpu_bound_task():
    # 计算密集型处理
    for _ in range(10000000):
        _ = 1 + 1

线程同步

当多个线程访问共享资源时,需要同步机制:

锁 (Lock)

counter = 0
counter_lock = threading.Lock()

def increment():
    global counter
    for _ in range(100000):
        with counter_lock:  # 等同于try/finally模式
            counter += 1

threads = [threading.Thread(target=increment) for _ in range(10)]
for t in threads:
    t.start()
for t in threads:
    t.join()

print(f"最终计数: {counter}")  # 总是1,000,000

其他同步原语

# 可重入锁
rlock = threading.RLock()

# 条件变量
condition = threading.Condition()
# 生产者
with condition:
    # 生产项目
    condition.notify()  # 通知消费者

# 信号量
semaphore = threading.Semaphore(3)  # 最多3个线程同时访问
with semaphore:
    # 访问有限资源

线程池

使用concurrent.futures模块可以简化线程池管理:

from concurrent.futures import ThreadPoolExecutor
import requests

urls = [
    "https://www.python.org",
    "https://www.google.com",
    "https://www.github.com",
    # 更多URL...
]

def fetch_url(url):
    response = requests.get(url)
    return f"{url}: {len(response.content)} bytes"

# 使用线程池并发获取URL
with ThreadPoolExecutor(max_workers=5) as executor:
    results = executor.map(fetch_url, urls)
    
    for result in results:
        print(result)

多线程 vs 多进程 vs 异步 IO

方法 适用场景 优点 缺点
多线程 I/O 密集型任务 共享内存、低开销 受 GIL 限制、同步复杂
多进程 CPU 密集型任务 利用多核、避开 GIL 内存开销大、通信复杂
异步 IO 高并发 I/O 单线程高并发、低开销 需要特殊编程模型、全栈都需要异步

典型应用场景

多线程最适合的场景:

Python 多线程虽然有 GIL 限制,但在正确的场景下使用仍然可以显著提高应用性能。