psi*_*dex 2 python asynchronous python-3.x async-await python-asyncio
例如
shared = {}
async def coro1():
# do r/w stuff with shared
async def coro2():
# do r/w stuff with shared
asyncio.create_task(coro1())
asyncio.create_task(coro2())
Run Code Online (Sandbox Code Playgroud)
如果coro1和coro2两个接入一个字典/变量,读取和写入,这需要某种形式的互斥/锁?或者它会很好,因为 asyncio 的东西只发生在 1 个线程上?
是的,你仍然需要锁。并发修改不会因为它是通过协程而不是线程发生而变得安全。
asyncio 有自己的专用asyncio.Lock,以及其他同步原语的自己版本,因为关心线程的锁不会相互保护协程,并且等待锁需要通过事件循环发生,而不是通过阻塞线。
shared = {}
lock = asyncio.Lock()
async def coro1():
...
async with lock:
await do_stuff_with(shared)
...
async def coro2():
...
async with lock:
await do_stuff_with(shared)
...
Run Code Online (Sandbox Code Playgroud)
也就是说,由于协程基于协作多任务处理而不是抢占式,有时您可以保证在线程需要锁的情况下不需要锁。例如,如果在临界区期间没有任何协程可以让出控制的点,那么您就不需要锁。
例如,这需要一个锁:
async def coro1():
async with lock:
for key in shared:
shared[key] = await do_something_that_could_yield(shared[key])
async def coro2():
async with lock:
for key in shared:
shared[key] = await do_something_that_could_yield(shared[key])
Run Code Online (Sandbox Code Playgroud)
这在技术上不会:
async def coro1():
for key in shared:
shared[key] = do_something_that_cant_yield(shared[key])
async def coro2():
for key in shared:
shared[key] = do_something_that_cant_yield(shared[key])
Run Code Online (Sandbox Code Playgroud)
但不锁定会随着代码更改而引入错误,特别是因为以下两个协程中确实需要锁定:
async def coro1():
async with lock:
for key in shared:
shared[key] = await do_something_that_could_yield(shared[key])
async def coro2():
async with lock:
for key in shared:
shared[key] = do_something_that_cant_yield(shared[key])
Run Code Online (Sandbox Code Playgroud)
如果两个协程都没有锁,coro2可能会coro1在coro1需要独占访问共享资源时中断。