Python多实例共享可变类属性读写更新时是否需要加锁?
类属性聚合统计场景下的锁使用判断
核心结论
要不要加锁完全由运行场景决定:
- 单线程环境下执行所有任务,不需要加锁,不存在竞争问题。
- 多线程并发调用不同实例的
do_task方法时,必须加锁,否则会出现统计数据错误。 - 多进程场景下不仅要加进程锁,还要替换类属性为进程间共享的数据结构,普通类属性在多进程下是各进程独立持有副本,根本无法实现全局聚合。
代码中两类操作的线程安全差异
你写的两个更新操作,本身的原子性是有区别的:
self.of_type_list.append(self.name):这是C层面实现的列表原子操作,在CPython的GIL(全局解释器锁)保护下,单步执行不会被线程切换打断,单独执行这行不会出现数据丢失。self.of_type_int += 1:这不是原子操作,实际执行分为三个步骤:读取当前类属性的整数值、值加1、把新值回写到类属性。多线程并发时,两个线程可能读到同一个旧值,各自加1后回写,最终统计值会比实际调用次数小,出现计数不准的问题。
哪怕append是原子操作,如果你需要保证「列表里的元素数量和int计数值永远一致」这个约束,两个操作合起来依然是非原子的,不加锁的话,其他线程可能在append完成、计数还没更新的间隙读到不一致的数据。
多线程场景的加锁实现
因为类属性是所有实例共享的,锁也需要定义为类级别的共享锁,所有实例抢同一把锁即可:
import threading class ClassAttrExample: of_type_list = [] of_type_int = 0 _data_lock = threading.Lock() def __init__(self, name): self.name = name def do_task(self): # 前置业务逻辑 with self._data_lock: self.of_type_list.append(self.name) self.of_type_int += 1
额外注意
如果其他逻辑需要读取这两个聚合统计值,读取时也要持有同一把锁,避免读到更新中间态的脏数据。
内容的提问来源于stack exchange,提问作者Conner M.
相关产品推荐
相关产品推荐

