
作为一个经常在代码世界里摸爬滚打的程序员,我发现“多线程”和“多进程”这两个词,就像武林中的“葵花宝典”和“九阳神功”,都很牛,但用不好就伤自己。
你可能已经听说过这样的说法:
这些说法有点道理,但并不全面。本文我将用通俗的方式+实战代码+实测对比,为你揭开“多线程 vs 多进程”的面纱,教你如何在不同场景下做出最优选择。
先说结论,多线程适合 I/O密集型任务,多进程适合 CPU密集型任务。为啥?
Python的“全局解释器锁(GIL)”限制了同一时刻只能有一个线程在执行 Python 字节码。所以在 CPU 密集型任务中,即使你开多个线程,实际也只有一个线程在工作,性能提升微乎其微。
但对于 I/O 密集型任务(比如网络请求、磁盘读写),线程在等待数据时会释放 GIL,其他线程可以趁机运行,从而实现“并发加速”。
我们分别用线程和进程来执行两个任务场景:
import threading
import multiprocessing
import time
import requests
def fetch_url():
requests.get("https://httpbin.org/delay/1") # 模拟网络延迟1秒
def run_threads():
threads = []
start = time.time()
for _ in range(10):
t = threading.Thread(target=fetch_url)
t.start()
threads.append(t)
for t in threads:
t.join()
print("线程耗时:", time.time() - start)
def run_processes():
processes = []
start = time.time()
for _ in range(10):
p = multiprocessing.Process(target=fetch_url)
p.start()
processes.append(p)
for p in processes:
p.join()
print("进程耗时:", time.time() - start)
if __name__ == '__main__':
print("=== I/O 密集型测试 ===")
run_threads()
run_processes()输出示例(大概时间):
线程耗时:约1.1秒
进程耗时:约2.8秒结论:线程完胜! 多线程由于开销小,能更快处理I/O等待任务。
def fib(n):
if n <= 2:
return 1
return fib(n - 1) + fib(n - 2)
def run_threads_cpu():
threads = []
start = time.time()
for _ in range(4):
t = threading.Thread(target=fib, args=(35,))
t.start()
threads.append(t)
for t in threads:
t.join()
print("线程耗时:", time.time() - start)
def run_processes_cpu():
processes = []
start = time.time()
for _ in range(4):
p = multiprocessing.Process(target=fib, args=(35,))
p.start()
processes.append(p)
for p in processes:
p.join()
print("进程耗时:", time.time() - start)
if __name__ == '__main__':
print("=== CPU 密集型测试 ===")
run_threads_cpu()
run_processes_cpu()输出示例:
线程耗时:约9秒
进程耗时:约2.8秒结论:进程大胜! CPU密集任务受GIL影响,线程间抢资源,反而更慢。
场景 | 优先选择 | 说明 |
|---|---|---|
网络请求、文件读写 | 多线程 | 等待时会释放GIL |
图像处理、数据分析、模型训练 | 多进程 | 多核优势明显 |
任务较多、启动频繁 | 多线程 | 线程开销小 |
任务耗时长、需要隔离性强 | 多进程 | 更稳定,不怕崩一个拖全家 |
想避免GIL影响 | 多进程 | 天生多核并发 |
concurrent.futuresPython 提供的标准库 ThreadPoolExecutor 和 ProcessPoolExecutor 简直就是并发神器,使用起来很优雅。
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
with ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(fetch_url, range(10)))
with ProcessPoolExecutor(max_workers=4) as executor:
results = list(executor.map(fib, [35]*4))import os
print(os.cpu_count()) # 用于设置多进程数问题 | 解决方案 |
|---|---|
子进程不能在 Jupyter Notebook 中用 | 使用 |
多线程无法加速 CPU 任务 | 换成多进程或使用 C 扩展,如 NumPy、Cython |
子线程未结束主线程就退出 | 使用 |
多线程和多进程不是对立的选择,而是各有优势的利器。关键在于你是否了解自己的任务类型——是大量的I/O?还是拼命卷CPU?
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。