Python中如何锁定一个key或者UUID?
我有多个线程会访问一个字典,会对某些 key 的值进行读,处理完了再写, 或者读了就不管了
我固然可以直接在字典读写这块的函数加 lock,但是感觉锁太重了
有没有办法能够让多个线程仅仅在同时访问,同一个 key 的时候再锁呢?
Python中如何锁定一个key或者UUID?
上策是修改你的程序架构,使得你不需要锁 key。
中策是预先创建 N 个锁(最好素数个),然后根据你的 key 算个哈希数值,模 N,锁对应的那个锁。
在Python中,没有内置的“锁定”某个特定键或UUID的机制。这通常指的是在并发环境下,确保对某个特定标识符(如字典的key或一个UUID字符串)关联的资源进行互斥访问。你需要使用线程锁或分布式锁来实现。
核心方法是:为每个需要独立锁定的key,动态地创建和管理一个专用的锁对象。
下面是一个使用threading模块为字典key实现细粒度锁定的示例:
import threading
from collections import defaultdict
import uuid
class KeyLock:
"""一个为不同key提供独立锁的简单管理器"""
def __init__(self):
# 使用defaultdict动态创建锁,避免重复创建
self._locks = defaultdict(threading.Lock)
# 用于保护_locks字典本身操作的锁,防止在创建新锁时出现竞态条件
self._meta_lock = threading.Lock()
def get_lock(self, key):
"""获取指定key对应的锁对象"""
# 注意:这里使用_meta_lock来保证对_locks字典的线程安全访问
with self._meta_lock:
return self._locks[key]
def __call__(self, key):
"""使其可被用作装饰器或上下文管理器"""
return self.get_lock(key)
# 使用示例
key_lock = KeyLock()
shared_dict = {}
def safe_write(key, value):
# 获取这个特定key的锁
lock = key_lock(key)
with lock:
# 临界区:对共享资源的操作
shared_dict[key] = value
print(f"Thread {threading.current_thread().name} wrote {value} to key {key}")
# 模拟多线程操作
def worker(key_vals):
for key, val in key_vals:
safe_write(key, val)
if __name__ == "__main__":
# 生成一些测试用的UUID作为key
keys = [str(uuid.uuid4()) for _ in range(3)]
# 创建两个线程,它们会操作重叠的key
import concurrent.futures
thread_data = [
[(keys[0], 'A1'), (keys[1], 'B1'), (keys[0], 'A2')], # 线程1会重复操作keys[0]
[(keys[1], 'B2'), (keys[0], 'A3'), (keys[2], 'C1')] # 线程2也会操作keys[0]和keys[1]
]
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
executor.map(worker, thread_data)
print("\nFinal shared_dict:", shared_dict)
关键点解释:
KeyLock类管理着一个锁字典 (_locks)。当你通过key_lock(key)请求一个锁时,它会返回该key独有的锁对象。- 对同一个key的并发访问,会被同一个锁阻塞,实现互斥。
- 对不同key的访问,使用不同的锁,互不干扰,最大化并发度。
- 内部的
_meta_lock用于保护锁字典本身,这是一个常见的“锁的锁”模式,确保在查找或创建key锁时的线程安全。
一句话总结:为每个需要锁定的key关联一个独立的锁对象来管理并发访问。
可以继承 UserDict 重写__getitem__方法
你说这样行不行?
我的字典
{
a: object_a
b: object_b
}
这两个对象我分别内置一个 lock = threading.Lock() 的实例,作为内置属性
然后获取到对象打算处理的时候,把对象的锁拿去来 acquire
建一个 lock dict 然后每个 key 一把锁,修改 getitem 之类的加锁?
话说,如果是 CPython 的话,是不是实现就是访问单个 object 就有全局锁?
可以。Python 的字典本身的读写可以看做原子操作,所以锁对象我觉得够了。
我是你肯定会加一个队列 FIFO 算了
的中策很靠谱。
memcahed 就是这么做的。
参考 openstack 的单例实现
用弱引用字典加双重判断和锁实现
更好的方式是修改这个字典用用专用线程做
请问这里为啥要素数个呢
使得每个锁被选中的概率都是 1/N。非素数的话,每个锁的概率在整个整数域上会有一点不平均。
hash 的常规操作吧,以前数据结构学散列表的时候经常这么用
这个答案好

