You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的作用是保证同一时间只有一个线程能执行被锁保护的代码块,但你只给写操作加了锁,读操作完全没参与锁的同步机制:

  1. 线程t1启动后,先获取锁,然后进入sleep(10),这10秒内锁被t1持有,但t2会立刻启动执行func2,直接去读persons["test3"]——此时t1还没完成键的添加,自然会触发KeyError。
  2. 锁只限制了写操作的排他性,但没有让读操作等待写操作完成,两个线程的执行顺序没有被锁同步。

解决方法

方法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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 05:48:23