Python 多线程
1. 多线程简介
多线程是 Python 中实现并发编程的一种方式。它允许程序在同一时间执行多个任务,特别适合处理 I/O 密集型操作。
1.1 线程与进程的区别
- 线程:是进程中的一个执行单元,共享进程的内存空间
- 进程:是程序的一次执行,拥有独立的内存空间
1.2 Python 中的 GIL
Python 的全局解释器锁(Global Interpreter Lock,GIL)是一个重要的概念:
- GIL 确保同一时刻只有一个线程执行 Python 字节码
- 这限制了 Python 多线程在 CPU 密集型任务上的性能
- 但对于 I/O 密集型任务,多线程仍然很有用
2. 创建和管理线程
2.1 使用 threading 模块
import threading
def worker():
print("工作线程正在运行")
# 创建线程
thread = threading.Thread(target=worker)
# 启动线程
thread.start()
# 等待线程完成
thread.join()
2.2 线程同步
2.2.1 使用锁(Lock)
import threading
lock = threading.Lock()
def safe_operation():
with lock:
# 临界区代码
pass
2.2.2 使用事件(Event)
import threading
event = threading.Event()
def wait_for_event():
event.wait() # 等待事件被设置
print("事件已触发")
thread = threading.Thread(target=wait_for_event)
thread.start()
event.set() # 触发事件
3. 线程池
使用 concurrent.futures 模块可以方便地管理线程池:
from concurrent.futures import ThreadPoolExecutor
def task(x):
return x * x
with ThreadPoolExecutor(max_workers=3) as executor:
results = executor.map(task, [1, 2, 3, 4, 5])
4. 最佳实践
4.1 何时使用多线程
- I/O 密集型任务(文件操作、网络请求等)
- 需要保持响应性的 GUI 应用
- 需要并行处理多个独立任务
4.2 注意事项
- 避免使用全局变量
- 使用线程安全的数据结构
- 正确处理线程同步
- 注意资源竞争问题
- 合理设置线程数量
5. 常见问题与解决方案
5.1 死锁预防
- 按固定顺序获取锁
- 使用超时机制
- 避免嵌套锁
5.2 性能优化
- 使用线程池而不是频繁创建新线程
- 合理设置线程数量
- 考虑使用
multiprocessing处理 CPU 密集型任务
6. 示例:文件下载器
import threading
import requests
from queue import Queue
class Downloader:
def __init__(self, num_threads=3):
self.queue = Queue()
self.threads = []
for _ in range(num_threads):
thread = threading.Thread(target=self._worker)
thread.start()
self.threads.append(thread)
def _worker(self):
while True:
url = self.queue.get()
if url is None:
break
try:
response = requests.get(url)
# 处理下载的文件
print(f"下载完成: {url}")
except Exception as e:
print(f"下载失败: {url}, 错误: {e}")
finally:
self.queue.task_done()
def add_url(self, url):
self.queue.put(url)
def wait_completion(self):
self.queue.join()
for _ in range(len(self.threads)):
self.queue.put(None)
for thread in self.threads:
thread.join()
7. 总结
Python 多线程是一个强大的工具,但需要谨慎使用。理解 GIL 的限制和线程同步的重要性是成功使用多线程的关键。对于不同的应用场景,选择合适的并发模型(多线程、多进程或异步编程)至关重要。
