Python中使用Lock类与threading修改字典时的问题排查
Python线程锁Lock的使用误区分析
你在测试Python的threading模块和Lock类时,遇到了这样的问题:在一个线程中耗时添加字典persons的键test3,另一个线程读取该键,已经给添加操作的func1加了锁,但运行时还是出现KeyError报错。
你的代码如下:
import threading from time import sleep persons = { "test": "test", "test2": "test2", } lock = threading.Lock() def func1(): global persons lock.acquire() sleep(10) persons["test3"] = "test3" lock.release() def func2(): global persons print(persons["test3"]) t1 = threading.Thread(target=func1) t2 = threading.Thread(target=func2) t1.start() t2.start() t2.join() t1.join()
报错信息:
Traceback (most recent call last): File "c:\Users\p.riahi\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 1038, in _bootstrap_inner self.run() File "c:\Users\p.riahi\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 975, in run self._target(*self._args, **self._kwargs) File "c:\Users\p.riahi\Desktop\temp\thred.py", line 20, in func2 print(persons["test3"]) ~~~~~~~^^^^^^^^^ KeyError: 'test3'
你对Lock的核心误区
Lock的作用是保证同一时间只有一个线程能执行被锁保护的代码块,但你只给写操作加了锁,读操作完全没参与锁的同步机制:
- 线程
t1启动后,先获取锁,然后进入sleep(10),这10秒内锁被t1持有,但t2会立刻启动执行func2,直接去读persons["test3"]——此时t1还没完成键的添加,自然会触发KeyError。 - 锁只限制了写操作的排他性,但没有让读操作等待写操作完成,两个线程的执行顺序没有被锁同步。
解决方法
方法1:读操作也加锁
让读操作也获取同一把锁,这样t2会等待t1释放锁后再执行,就能读到已经添加的键:
import threading from time import sleep persons = { "test": "test", "test2": "test2", } lock = threading.Lock() def func1(): global persons lock.acquire() sleep(10) persons["test3"] = "test3" lock.release() def func2(): global persons lock.acquire() print(persons["test3"]) lock.release() t1 = threading.Thread(target=func1) t2 = threading.Thread(target=func2) t1.start() t2.start() t2.join() t1.join()
方法2:用Event实现线程同步
如果不需要锁保护读写,只是想让读线程等写线程完成后再执行,可以用threading.Event:
import threading from time import sleep persons = { "test": "test", "test2": "test2", } write_done = threading.Event() def func1(): global persons sleep(10) persons["test3"] = "test3" write_done.set() # 标记写操作完成 def func2(): global persons write_done.wait() # 等待写操作完成的信号 print(persons["test3"]) t1 = threading.Thread(target=func1) t2 = threading.Thread(target=func2) t1.start() t2.start() t2.join() t1.join()
方法3:读操作做存在性判断(仅避免报错)
如果只是不想触发KeyError,可以在读的时候先判断键是否存在,但这种方式不能保证读到最新的写入值,只是避免报错:
def func2(): global persons print(persons.get("test3", "键不存在"))
内容的提问来源于stack exchange,提问作者parsariyahi
相关产品推荐
相关产品推荐

