3848 字
19 分钟
Python 并发编程完全指南:threading / multiprocessing / concurrent.futures 深度解析与实战
Python 并发编程是提升程序性能的关键技能。由于 GIL 的存在,Python 的并发编程与其他语言有显著差异。理解 threading、multiprocessing 和 concurrent.futures 的适用场景,是写出高效 Python 并发程序的基础。

本文全面讲解 Python 并发编程:
- GIL 原理与影响
- threading 多线程编程
- multiprocessing 多进程编程
- concurrent.futures 线程池与进程池
- 同步原语(Lock / RLock / Semaphore / Event / Condition)
- Queue 线程安全队列
- 进程间通信(Pipe / Queue / SharedMemory)
- CPU 密集型 vs IO 密集型策略选择
- 实战场景与最佳实践
一、GIL 原理与影响
1.1 什么是 GIL
# GIL(Global Interpreter Lock)是 CPython 中的一个互斥锁# 确保同一时刻只有一个线程执行 Python 字节码
# 验证 GIL 的存在import threadingimport time
def count_down(n): while n > 0: n -= 1
# 单线程start = time.time()count_down(100_000_000)single_time = time.time() - startprint(f"单线程: {single_time:.2f}s")
# 多线程(受 GIL 限制,不会加速)t1 = threading.Thread(target=count_down, args=(50_000_000,))t2 = threading.Thread(target=count_down, args=(50_000_000,))
start = time.time()t1.start()t2.start()t1.join()t2.join()multi_time = time.time() - startprint(f"多线程: {multi_time:.2f}s")
# 结果:多线程可能更慢(线程切换开销)# 单线程: 3.21s# 多线程: 3.45s1.2 GIL 的影响范围
# GIL 在以下场景中不影响性能:
# 1. IO 操作(自动释放 GIL)import urllib.request
def fetch_url(url): response = urllib.request.urlopen(url) return response.read()
# 2. time.sleep(释放 GIL)import timetime.sleep(1) # 其他线程可以执行
# 3. C 扩展(可手动释放 GIL)import numpy as np# NumPy 底层在计算时释放 GIL
# GIL 在以下场景中严重影响性能:# 1. CPU 密集型 Python 代码# 2. 纯 Python 循环计算# 3. 大量 Python 对象操作1.3 应对策略
# 策略选择决策树"""任务类型?├── IO 密集型(网络/文件/数据库)│ ├── 低并发 → threading│ ├── 高并发 → asyncio│ └── 简单并行 → ThreadPoolExecutor├── CPU 密集型(计算/加密/图像处理)│ ├── 简单并行 → ProcessPoolExecutor│ ├── 需要精细控制 → multiprocessing│ └── 大规模数据 → multiprocessing + SharedMemory└── 混合型 └── ThreadPoolExecutor + ProcessPoolExecutor 组合"""二、threading 多线程编程
2.1 创建线程
import threadingimport time
# 方法1:函数方式def worker(name, delay): print(f"线程 {name} 开始") time.sleep(delay) print(f"线程 {name} 完成")
t1 = threading.Thread(target=worker, args=("A", 2))t2 = threading.Thread(target=worker, args=("B", 3))
t1.start() # 启动线程t2.start()
t1.join() # 等待线程完成t2.join()print("所有线程完成")
# 方法2:继承 Thread 类class MyThread(threading.Thread): def __init__(self, name, delay): super().__init__() self.name = name self.delay = delay
def run(self): print(f"线程 {self.name} 开始") time.sleep(self.delay) print(f"线程 {self.name} 完成")
t = MyThread("C", 2)t.start()t.join()2.2 线程状态与生命周期
import threading
def task(): import time time.sleep(2)
t = threading.Thread(target=task)
# 线程状态print(t.is_alive()) # False(未启动)print(t.daemon) # False(非守护线程)
t.start()print(t.is_alive()) # True(运行中)print(t.name) # Thread-1print(t.ident) # 线程ID
t.join() # 等待完成print(t.is_alive()) # False
# 守护线程(主线程退出时自动结束)daemon_t = threading.Thread(target=task, daemon=True)# 或daemon_t = threading.Thread(target=task)daemon_t.daemon = Truedaemon_t.start()# 主线程退出时,daemon_t 会被强制终止2.3 获取线程返回值
import threading
# 方法1:共享变量def worker(result, key): result[key] = "computed value"
result = {}t = threading.Thread(target=worker, args=(result, "data"))t.start()t.join()print(result["data"]) # "computed value"
# 方法2:继承 Thread,重写 runclass ResultThread(threading.Thread): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.result = None
def run(self): if self._target: self.result = self._target(*self._args, **self._kwargs)
def compute(x, y): return x + y
t = ResultThread(target=compute, args=(10, 20))t.start()t.join()print(t.result) # 30三、同步原语
3.1 Lock(互斥锁)
import threading
# 问题:线程不安全counter = 0
def unsafe_increment(): global counter for _ in range(1_000_000): counter += 1 # 非原子操作
t1 = threading.Thread(target=unsafe_increment)t2 = threading.Thread(target=unsafe_increment)t1.start()t2.start()t1.join()t2.join()print(f"预期: 2000000, 实际: {counter}") # 实际 < 2000000
# 解决:使用 Lockcounter = 0lock = threading.Lock()
def safe_increment(): global counter for _ in range(1_000_000): with lock: # 自动获取和释放锁 counter += 1
t1 = threading.Thread(target=safe_increment)t2 = threading.Thread(target=safe_increment)t1.start()t2.start()t1.join()t2.join()print(f"预期: 2000000, 实际: {counter}") # 2000000 ✅3.2 RLock(可重入锁)
import threading
# Lock 的问题:同一线程不能多次获取lock = threading.Lock()lock.acquire()# lock.acquire() # 死锁!
# RLock 解决:同一线程可多次获取rlock = threading.RLock()rlock.acquire()rlock.acquire() # ✅ 同一线程可以多次获取rlock.release()rlock.release()
# 实际应用:递归调用class BankAccount: def __init__(self, balance=0): self.balance = balance self.lock = threading.RLock()
def deposit(self, amount): with self.lock: self.balance += amount
def withdraw(self, amount): with self.lock: if self.balance >= amount: self.balance -= amount return True return False
def transfer(self, other, amount): with self.lock: # 外层加锁 if self.withdraw(amount): # 内层也需要锁 → RLock other.deposit(amount) return True return False3.3 Semaphore(信号量)
import threadingimport timeimport random
# 限制同时访问的线程数(如数据库连接池)semaphore = threading.Semaphore(3) # 最多 3 个线程同时执行
def worker(worker_id): print(f"Worker {worker_id} 等待获取信号量") with semaphore: print(f"Worker {worker_id} 开始工作") time.sleep(random.uniform(0.5, 2.0)) print(f"Worker {worker_id} 完成")
threads = [threading.Thread(target=worker, args=(i,)) for i in range(10)]for t in threads: t.start()for t in threads: t.join()
# 同时只有 3 个 Worker 在工作3.4 Event(事件)
import threadingimport time
# Event:线程间简单通信event = threading.Event()
def waiter(name): print(f"{name} 等待事件...") event.wait() # 阻塞直到 event.set() print(f"{name} 收到事件,继续执行")
def setter(): time.sleep(2) print("设置事件") event.set()
# 启动等待者for name in ["A", "B", "C"]: threading.Thread(target=waiter, args=(name,)).start()
# 启动设置者threading.Thread(target=setter).start()
# 2 秒后所有等待者同时继续执行3.5 Condition(条件变量)
import threadingimport timeimport random
# 生产者-消费者模式class BoundedBuffer: def __init__(self, capacity): self.buffer = [] self.capacity = capacity self.condition = threading.Condition()
def put(self, item): with self.condition: while len(self.buffer) >= self.capacity: self.condition.wait() # 缓冲区满,等待 self.buffer.append(item) self.condition.notify() # 通知消费者
def get(self): with self.condition: while not self.buffer: self.condition.wait() # 缓冲区空,等待 item = self.buffer.pop(0) self.condition.notify() # 通知生产者 return item
buffer = BoundedBuffer(5)
def producer(id): for i in range(10): item = f"产品-{id}-{i}" buffer.put(item) print(f"生产: {item}") time.sleep(random.uniform(0.1, 0.5))
def consumer(id): for _ in range(10): item = buffer.get() print(f" 消费({id}): {item}") time.sleep(random.uniform(0.2, 0.8))
# 启动生产者和消费者for i in range(2): threading.Thread(target=producer, args=(i,)).start()for i in range(3): threading.Thread(target=consumer, args=(i,)).start()四、Queue 线程安全队列
4.1 基本队列
import queueimport threadingimport time
# Queue:线程安全的 FIFO 队列q = queue.Queue(maxsize=10)
def producer(): for i in range(20): q.put(f"任务-{i}") print(f"生产: 任务-{i}") time.sleep(0.1) q.put(None) # 结束信号
def consumer(id): while True: item = q.get() # 阻塞获取 if item is None: q.put(None) # 传递结束信号给其他消费者 break print(f" 消费者-{id}: 处理 {item}") time.sleep(0.3) q.task_done() # 标记任务完成
# 启动threading.Thread(target=producer).start()for i in range(3): threading.Thread(target=consumer, args=(i,)).start()4.2 队列类型
import queue
# FIFO 队列(先进先出)fifo = queue.Queue()fifo.put(1)fifo.put(2)print(fifo.get()) # 1
# LIFO 队列(后进先出,栈)lifo = queue.LifoQueue()lifo.put(1)lifo.put(2)print(lifo.get()) # 2
# 优先级队列pq = queue.PriorityQueue()pq.put((3, "低优先级"))pq.put((1, "高优先级"))pq.put((2, "中优先级"))print(pq.get()[1]) # "高优先级"print(pq.get()[1]) # "中优先级"print(pq.get()[1]) # "低优先级"4.3 线程池工作队列模式
import queueimport threadingimport timeimport requests
class ThreadPool: """简易线程池"""
def __init__(self, num_threads): self.tasks = queue.Queue() self.threads = [] self.running = True
for _ in range(num_threads): t = threading.Thread(target=self._worker) t.daemon = True t.start() self.threads.append(t)
def _worker(self): while self.running: try: func, args, callback = self.tasks.get(timeout=1) result = func(*args) if callback: callback(result) self.tasks.task_done() except queue.Empty: continue
def submit(self, func, *args, callback=None): self.tasks.put((func, args, callback))
def shutdown(self): self.running = False for t in self.threads: t.join()
# 使用pool = ThreadPool(4)
def fetch(url): response = requests.get(url) return len(response.text)
def on_complete(result): print(f"获取到 {result} 字节")
urls = ["https://httpbin.org/get"] * 5for url in urls: pool.submit(fetch, url, callback=on_complete)
time.sleep(5)pool.shutdown()五、multiprocessing 多进程
5.1 创建进程
import multiprocessingimport time
def worker(name, delay): print(f"进程 {name} 开始") time.sleep(delay) print(f"进程 {name} 完成")
if __name__ == '__main__': p1 = multiprocessing.Process(target=worker, args=("A", 2)) p2 = multiprocessing.Process(target=worker, args=("B", 3))
p1.start() p2.start()
p1.join() p2.join() print("所有进程完成")5.2 CPU 密集型任务对比
import threadingimport multiprocessingimport timeimport math
def cpu_intensive(n): """CPU 密集型任务:计算质数""" count = 0 for i in range(2, n): is_prime = True for j in range(2, int(math.sqrt(i)) + 1): if i % j == 0: is_prime = False break if is_prime: count += 1 return count
# 单线程start = time.time()result1 = cpu_intensive(500_000)result2 = cpu_intensive(500_000)single_time = time.time() - startprint(f"单线程: {single_time:.2f}s")
# 多线程(受 GIL 限制,无加速)start = time.time()t1 = threading.Thread(target=cpu_intensive, args=(500_000,))t2 = threading.Thread(target=cpu_intensive, args=(500_000,))t1.start()t2.start()t1.join()t2.join()thread_time = time.time() - startprint(f"多线程: {thread_time:.2f}s")
# 多进程(真正并行,接近 2 倍加速)if __name__ == '__main__': start = time.time() p1 = multiprocessing.Process(target=cpu_intensive, args=(500_000,)) p2 = multiprocessing.Process(target=cpu_intensive, args=(500_000,)) p1.start() p2.start() p1.join() p2.join() process_time = time.time() - start print(f"多进程: {process_time:.2f}s")
# 结果示例: # 单线程: 8.52s # 多线程: 8.71s(无加速,甚至更慢) # 多进程: 4.38s(接近 2 倍加速)5.3 获取进程返回值
import multiprocessing
def compute(x, y): return x * y
if __name__ == '__main__': # 方法1:使用 Queue result_queue = multiprocessing.Queue()
def worker_with_queue(x, y, q): q.put(compute(x, y))
p = multiprocessing.Process( target=worker_with_queue, args=(10, 20, result_queue) ) p.start() p.join() print(result_queue.get()) # 200
# 方法2:使用 Pool(推荐) with multiprocessing.Pool() as pool: result = pool.apply(compute, (10, 20)) print(result) # 200六、进程间通信
6.1 Pipe(管道)
import multiprocessing
def sender(conn): conn.send("Hello from sender") data = conn.recv() print(f"Sender received: {data}") conn.close()
if __name__ == '__main__': parent_conn, child_conn = multiprocessing.Pipe()
p = multiprocessing.Process(target=sender, args=(child_conn,)) p.start()
print(parent_conn.recv()) # "Hello from sender" parent_conn.send("Hello from parent")
p.join()6.2 Queue(进程队列)
import multiprocessingimport time
def producer(q): for i in range(5): q.put(f"产品-{i}") print(f"生产: 产品-{i}") time.sleep(0.5) q.put(None) # 结束信号
def consumer(q): while True: item = q.get() if item is None: break print(f"消费: {item}") time.sleep(1)
if __name__ == '__main__': q = multiprocessing.Queue(maxsize=10)
p1 = multiprocessing.Process(target=producer, args=(q,)) p2 = multiprocessing.Process(target=consumer, args=(q,))
p1.start() p2.start()
p1.join() p2.join()6.3 Value 和 Array(共享内存)
import multiprocessing
def increment_counter(counter, lock): for _ in range(100_000): with lock: counter.value += 1
if __name__ == '__main__': # 共享整数 counter = multiprocessing.Value('i', 0) lock = multiprocessing.Lock()
processes = [ multiprocessing.Process(target=increment_counter, args=(counter, lock)) for _ in range(4) ]
for p in processes: p.start() for p in processes: p.join()
print(f"最终值: {counter.value}") # 400000
# 共享数组def modify_array(arr, lock): for i in range(len(arr)): with lock: arr[i] += 1
if __name__ == '__main__': arr = multiprocessing.Array('i', [0, 0, 0, 0, 0]) lock = multiprocessing.Lock()
processes = [ multiprocessing.Process(target=modify_array, args=(arr, lock)) for _ in range(3) ]
for p in processes: p.start() for p in processes: p.join()
print(f"数组: {list(arr)}") # [3, 3, 3, 3, 3]6.4 Manager(共享对象)
import multiprocessing
def worker(d, key, value): d[key] = value
if __name__ == '__main__': with multiprocessing.Manager() as manager: # 共享字典 shared_dict = manager.dict()
processes = [] for i in range(5): p = multiprocessing.Process( target=worker, args=(shared_dict, f"key_{i}", f"value_{i}") ) processes.append(p) p.start()
for p in processes: p.join()
print(dict(shared_dict)) # {'key_0': 'value_0', 'key_1': 'value_1', ...}
# Manager 还支持 list, dict, Namespace, Lock 等 shared_list = manager.list() shared_list.append(1)七、concurrent.futures(推荐)
7.1 ThreadPoolExecutor
from concurrent.futures import ThreadPoolExecutor, as_completedimport requests
# 线程池:IO 密集型任务def fetch_url(url): response = requests.get(url, timeout=10) return url, response.status_code, len(response.text)
urls = [ "https://httpbin.org/get", "https://httpbin.org/delay/1", "https://httpbin.org/delay/2", "https://httpbin.org/ip", "https://httpbin.org/uuid",]
# 方法1:submit + as_completed(灵活)with ThreadPoolExecutor(max_workers=3) as executor: futures = {executor.submit(fetch_url, url): url for url in urls}
for future in as_completed(futures): url = futures[future] try: result = future.result() print(f"{result[0]}: {result[1]} ({result[2]} bytes)") except Exception as e: print(f"{url} 错误: {e}")
# 方法2:map(简单,按顺序返回)with ThreadPoolExecutor(max_workers=3) as executor: results = executor.map(fetch_url, urls) for result in results: print(f"{result[0]}: {result[1]} ({result[2]} bytes)")7.2 ProcessPoolExecutor
from concurrent.futures import ProcessPoolExecutor, as_completedimport math
# 进程池:CPU 密集型任务def is_prime(n): if n < 2: return False for i in range(2, int(math.sqrt(n)) + 1): if n % i == 0: return False return True
def count_primes(start, end): count = 0 for i in range(start, end): if is_prime(i): count += 1 return count
if __name__ == '__main__': # 将任务分成 4 块 ranges = [ (2, 250_000), (250_000, 500_000), (500_000, 750_000), (750_000, 1_000_000), ]
with ProcessPoolExecutor(max_workers=4) as executor: futures = { executor.submit(count_primes, start, end): (start, end) for start, end in ranges }
total = 0 for future in as_completed(futures): count = future.result() total += count print(f"区块 {futures[future]}: {count} 个质数")
print(f"总计: {total} 个质数")7.3 回调函数
from concurrent.futures import ThreadPoolExecutorimport requests
def fetch(url): response = requests.get(url) return response.json()
def on_success(future): """任务完成回调""" try: data = future.result() print(f"成功: {data.get('url', 'unknown')}") except Exception as e: print(f"失败: {e}")
with ThreadPoolExecutor(max_workers=3) as executor: future = executor.submit(fetch, "https://httpbin.org/get") future.add_done_callback(on_success)
# 主线程可以继续做其他事 print("任务已提交")7.4 统一接口切换
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def task(n): import time time.sleep(1) return n * n
# 只需改一行即可切换线程池/进程池def run(executor_class, tasks): with executor_class(max_workers=4) as executor: results = list(executor.map(task, tasks)) return results
tasks = list(range(10))
# IO 密集型 → 用线程池results = run(ThreadPoolExecutor, tasks)print(results)
# CPU 密集型 → 用进程池if __name__ == '__main__': results = run(ProcessPoolExecutor, tasks) print(results)八、实战场景
8.1 并发网页爬虫
from concurrent.futures import ThreadPoolExecutor, as_completedimport requestsfrom bs4 import BeautifulSoupimport time
def crawl_page(url): try: response = requests.get(url, timeout=10) soup = BeautifulSoup(response.text, 'html.parser') title = soup.find('title') return { 'url': url, 'status': response.status_code, 'title': title.text if title else 'N/A', 'length': len(response.text), } except Exception as e: return {'url': url, 'error': str(e)}
def batch_crawl(urls, max_workers=10): results = []
with ThreadPoolExecutor(max_workers=max_workers) as executor: futures = {executor.submit(crawl_page, url): url for url in urls}
for future in as_completed(futures): result = future.result() results.append(result) if 'error' not in result: print(f"[{result['status']}] {result['title']} - {result['url']}")
return results
# 使用urls = [f"https://httpbin.org/delay/{i % 3}" for i in range(20)]
start = time.time()results = batch_crawl(urls, max_workers=10)print(f"\n爬取 {len(urls)} 个页面,耗时: {time.time() - start:.2f}s")8.2 并行数据处理
from concurrent.futures import ProcessPoolExecutorimport time
def process_chunk(data_chunk): """处理数据块(CPU 密集型)""" result = [] for item in data_chunk: # 模拟计算密集型操作 value = sum(i * i for i in range(item)) result.append(value) return result
def parallel_process(data, num_workers=4): """并行处理大数据集""" # 分块 chunk_size = len(data) // num_workers chunks = [ data[i:i + chunk_size] for i in range(0, len(data), chunk_size) ]
# 并行处理 with ProcessPoolExecutor(max_workers=num_workers) as executor: results = list(executor.map(process_chunk, chunks))
# 合并结果 return [item for chunk in results for item in chunk]
if __name__ == '__main__': data = list(range(1000, 2000))
start = time.time() result = parallel_process(data, num_workers=4) print(f"并行处理 {len(data)} 项,耗时: {time.time() - start:.2f}s")8.3 速率限制并发
import threadingimport timeimport requestsfrom concurrent.futures import ThreadPoolExecutor
class RateLimiter: """令牌桶速率限制器"""
def __init__(self, rate, capacity=None): self.rate = rate # 每秒生成的令牌数 self.capacity = capacity or rate self.tokens = self.capacity self.last_update = time.time() self.lock = threading.Lock()
def acquire(self): with self.lock: now = time.time() elapsed = now - self.last_update self.tokens = min( self.capacity, self.tokens + elapsed * self.rate ) self.last_update = now
if self.tokens >= 1: self.tokens -= 1 return True return False
def rate_limited_fetch(url, rate_limiter): while not rate_limiter.acquire(): time.sleep(0.01)
response = requests.get(url) return url, response.status_code
# 每秒最多 5 个请求rate_limiter = RateLimiter(rate=5)urls = [f"https://httpbin.org/get?i={i}" for i in range(20)]
with ThreadPoolExecutor(max_workers=10) as executor: futures = [ executor.submit(rate_limited_fetch, url, rate_limiter) for url in urls ]
for future in futures: url, status = future.result() print(f"{status} - {url}")九、常见陷阱与最佳实践
❌ 陷阱 1:竞态条件
import threading
# ❌ 线程不安全balance = 0
def transfer(amount): global balance # 以下操作非原子性 current = balance time.sleep(0.001) # 模拟延迟 balance = current + amount
# ✅ 使用锁保护共享资源balance = 0lock = threading.Lock()
def safe_transfer(amount): global balance with lock: current = balance balance = current + amount❌ 陷阱 2:死锁
import threading
# ❌ 死锁:锁的获取顺序不一致lock1 = threading.Lock()lock2 = threading.Lock()
def task_a(): with lock1: time.sleep(0.1) with lock2: # 等待 lock2 print("A done")
def task_b(): with lock2: time.sleep(0.1) with lock1: # 等待 lock1 → 死锁 print("B done")
# ✅ 修复:统一锁的获取顺序def task_a_fixed(): with lock1: with lock2: print("A done")
def task_b_fixed(): with lock1: # 先获取 lock1 with lock2: # 再获取 lock2 print("B done")❌ 陷阱 3:守护线程中的资源泄漏
import threading
# ❌ 守护线程可能被强制终止,导致资源未释放def write_file(): with open("data.txt", "w") as f: while True: f.write("data\n") time.sleep(1)
# daemon=True 的线程在主线程退出时会被强制终止# 可能导致文件未正确关闭
# ✅ 使用非守护线程 + 优雅退出def write_file_safe(stop_event): with open("data.txt", "w") as f: while not stop_event.is_set(): f.write("data\n") f.flush() time.sleep(1)
stop = threading.Event()t = threading.Thread(target=write_file_safe, args=(stop,))t.start()
# 优雅退出stop.set()t.join()✅ 最佳实践
| 原则 | 说明 |
|---|---|
| IO 密集用线程 | 网络请求、文件读写 |
| CPU 密集用进程 | 计算、加密、图像处理 |
| 优先用 concurrent.futures | 统一 API,简化代码 |
| 避免共享状态 | 使用 Queue 传递数据 |
| 锁要最小化 | 只锁必要的代码段 |
| 统一锁顺序 | 避免死锁 |
| 设置超时 | 避免永久阻塞 |
| 使用上下文管理器 | 自动释放资源 |
| 进程池注意序列化 | 参数必须可 pickle |
十、总结
并发方案对比
| 方案 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|
| threading | IO 密集型 | 轻量、共享内存 | GIL 限制 |
| multiprocessing | CPU 密集型 | 真正并行 | 内存开销大 |
| concurrent.futures | 通用 | 统一 API、简化代码 | 灵活性略低 |
| asyncio | 高并发 IO | 单线程高并发 | 需要异步库支持 |
选型决策
需要并发?├── IO 密集型│ ├── 简单场景 → ThreadPoolExecutor│ ├── 高并发 → asyncio│ └── 需要同步代码 → threading + Queue├── CPU 密集型│ ├── 简单并行 → ProcessPoolExecutor│ ├── 大数据 → multiprocessing + SharedMemory│ └── 数值计算 → NumPy(释放 GIL)└── 混合型 └── ThreadPoolExecutor + ProcessPoolExecutorPython 并发编程的核心是理解 GIL 的影响并选择正确的工具。IO 密集型用线程,CPU 密集型用进程,简单场景用 concurrent.futures。掌握同步原语和进程间通信,才能写出安全高效的并发程序。2026 年,concurrent.futures 已成为 Python 并发编程的首选方案,推荐优先使用。
Python 并发编程完全指南:threading / multiprocessing / concurrent.futures 深度解析与实战
https://971918.xyz/posts/python-guide/python-concurrency-guide/