python线程安全核心是避免竞态条件,主要用threading.lock保护共享变量和queue.queue替代手动共享列表;lock需配合with语句确保自动释放,queue的put/get天生线程安全且支持阻塞、超时与任务完成通知;需注意锁顺序、避免耗时操作和死锁。

Python中实现线程安全,核心是避免多个线程同时修改共享数据导致竞态条件。最常用、最实用的方式是用 threading.Lock 控制临界区,以及用 queue.Queue 替代手动管理共享列表——后者天生线程安全,无需额外加锁。
用 Lock 保护共享变量
当多个线程需读写同一个变量(如计数器、字典、列表)时,必须用锁确保同一时刻只有一个线程能执行操作。
说明:Lock 是最基础的同步原语,调用 acquire() 上锁,release() 解锁。推荐用 with 语句,自动处理异常时的释放。
示例:两个线程对全局计数器累加 10 万次,不加锁会丢失更新;加锁后结果准确。
立即学习“Python免费学习笔记(深入)”;
import threading import time <p>counter = 0 lock = threading.Lock()</p><p>def increment(): global counter for _ in range(100000): with lock: # 自动 acquire/release counter += 1</p><p>t1 = threading.Thread(target=increment) t2 = threading.Thread(target=increment) t1.start(); t2.start() t1.join(); t2.join() print(counter) # 输出 200000(加锁后稳定)
用 Queue 替代手动共享列表
如果线程间需要传递数据(比如生产者往里放、消费者从中取),别用 list + Lock 手动同步——容易出错且低效。直接用 queue.Queue,它的 put() 和 get() 方法内部已加锁,还支持阻塞、超时、任务完成通知等特性。
常见使用场景:日志收集、任务分发、异步结果汇总。
建议:
- 生产者调用
q.put(item),消费者调用q.get(),都不需额外加锁 - 用
q.task_done()标记单个任务完成,配合q.join()等待全部处理完毕 - 设置
maxsize可控制队列容量,避免内存无限增长
避免死锁与常见陷阱
锁用不好反而引发死锁或性能瓶颈。注意以下几点:
- 始终按相同顺序获取多个锁(例如先 lock_a 再 lock_b),否则可能 A 持有 a 等 b,B 持有 b 等 a
- 不要在持有锁时做耗时操作(如网络请求、sleep),会阻塞其他线程
- 避免在锁内调用可能再次申请同一锁的函数(递归锁除外,可用
RLock) - Queue 的
get()默认阻塞,若需非阻塞,用get_nowait()或设timeout
实际组合:生产者-消费者模型
一个典型线程安全实践:多个生产者生成任务,多个消费者并发处理,用 Queue 中转,用 Lock 记录统计信息。
关键点:
- 任务队列用
queue.Queue,天然安全 - 全局统计(如成功数、失败数)用
threading.Lock保护 - 消费者处理完调用
task_done(),主线程用join()等待结束
这种结构清晰、扩展性强,是 Python 多线程服务的常用骨架。







