在Python中使用不同的键并发写入字典

M. *_*eng 6 python dictionary coroutine

我正在编写一个简单的 Python 程序,它使用 asyncio.coroutines 维护一个字典。其设计是每个协程向字典添加一个具有不同键的条目。我的问题是,修改字典时是否需要同步以避免竞争条件?换句话说,写入字典是原子的吗?

这是我的程序:

map ={}
key_set = Set(...)

@asyncio.coroutine
def update(key):
    # do I need synchronization here to avoid race condition?
    map[key] = ... # add a new entry with key to the map

fut = []
for key in key_set:
    fut.append(update(key))

loop = asyncio.get_event_loop()
loop.run_until_complete(
    asyncio.gather(*fut)
)
loop.close()
Run Code Online (Sandbox Code Playgroud)

jsb*_*eno 6

是的 - 字典的写入是原子的。在异步模型中,即使它们不是线程安全的,除非您在某处显式使用线程池,否则不存在“真正的并发” - 您的代码可能暂停的所有点都由“异步命令”很好地标记。

Python 中采用的异步模型的巨大优势在于,对于所有 I/O 绑定代码,您可以获得等效的并行性,而无需并行性的复杂性:await 和其他命令是函数代码产生的唯一执行点其他代码运行的权限 - 因此,即使您正在处理需要隔离的复杂数据结构,您所需要的只是将所有代码放在中间,而无需异步调用。

在您给出的示例中,update代码永远不会被另一个协程中断,因为没有明确的暂停。您甚至可以添加一些逻辑来检查您是否没有覆盖由协同例程的另一个实例编写的相同密钥,这仍然很好(但对于多线程代码,您需要一个锁):

@asyncio.coroutine
def update(key):
    if key not in map:
         map[key] = ... # add a new entry with key to the map
    else:
         logger.info(f'"{key}" already in use.')
Run Code Online (Sandbox Code Playgroud)

但即使你的代码是多线程的,Python 中的字典仍然是线程安全的。由于臭名昭著的全局解释器锁(GIL)——几十年来它一直在并行代码中阻碍Python——但代价正是字典和列表等数据结构的线程安全性。