并发编程入门
并发编程入门
Python并发编程是处理多任务的核心技术。理解GIL的限制,以及何时选择多线程或多进程,是编写高效并发程序的关键。
GIL(全局解释器锁)
GIL是CPython解释器中的一个互斥锁,确保同一时刻只有一个线程执行Python字节码:
import threading
import time
counter = 0
def count_up():
global counter
for _ in range(100000):
counter += 1
# 多线程不安全!
threads = [threading.Thread(target=count_up) for _ in range(4)]
for t in threads:
t.start()
for t in threads:
t.join()
print(counter) # 结果不确定,小于400000
GIL的存在意味着CPU密集型任务使用多线程无法真正并行。
threading 模块
多线程适合I/O密集型任务:
import threading
import time
def io_task(name, duration):
"""模拟I/O操作"""
print(f"{name} 开始")
time.sleep(duration)
print(f"{name} 完成")
# 创建线程
threads = []
for i in range(3):
t = threading.Thread(target=io_task, args=(f"线程{i}", 2))
threads.append(t)
t.start()
for t in threads:
t.join()
print("所有线程完成")
# 总耗时约2秒,而非6秒
multiprocessing 模块
多进程适合CPU密集型任务:
import multiprocessing
import time
def cpu_task(n):
"""CPU密集型任务"""
return sum(i * i for i in range(n))
if __name__ == "__main__":
numbers = [10**7] * 4
start = time.time()
results = [cpu_task(n) for n in numbers]
print(f"串行: {time.time() - start:.2f}秒")
start = time.time()
with multiprocessing.Pool(4) as pool:
results = pool.map(cpu_task, numbers)
print(f"并行: {time.time() - start:.2f}秒")
threading vs multiprocessing
| 特性 | threading | multiprocessing |
|---|---|---|
| 内存共享 | 共享内存 | 独立内存空间 |
| 适用场景 | I/O密集型 | CPU密集型 |
| 创建开销 | 小 | 大 |
| 通信方式 | 共享变量 | Queue/Pipe |
| GIL影响 | 受GIL限制 | 不受GIL限制 |
线程安全
import threading
import time
# 使用锁保证线程安全
lock = threading.Lock()
counter = 0
def safe_count():
global counter
for _ in range(100000):
with lock:
counter += 1
threads = [threading.Thread(target=safe_count) for _ in range(4)]
for t in threads:
t.start()
for t in threads:
t.join()
print(counter) # 400000
基本选择原则
- I/O密集型(网络请求、文件读写、数据库查询)→ 使用
threading - CPU密集型(计算、加密、图像处理)→ 使用
multiprocessing - 需要共享状态 → 使用
threading(注意加锁) - 需要隔离 → 使用
multiprocessing
理解并发编程的基础概念,是掌握更高级异步编程的前提。