import asyncio
l = asyncio.Lock()
async def test():
print('locked' if l.locked() else 'unlocked')
await l.acquire()
# await asyncio.ensure_future(l.acquire())
await asyncio.sleep(1)
l.release()
async def main():
await asyncio.gather(test(), test())
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
We start two test()
coroutines, first of them immediately locks Lock by l.acquire()
, second prints locked
state. Output:
unlocked
locked
Everything changes if you will comment await l.acquire()
line and uncomment next one. Output will be:
unlocked
unlocked
It happens because l.acquire()
wrapped in Task starts after second test()
was started.
Is there any way to make l.acquire()
Task to be started as soon as possible, before second test()
(and to get same output as in the original code)?
Looks like I found solution. We need global lock that would suspend second task locked checking while first task starts:
Output:
But here we get another problem: second task would be suspended while first task completely done, that's why we get 1 second delay before second locking. It was hard to see in original example (because
test()
wait for single lock), that's why I added second printing. But it can be important for multiple locking of different resources to start coroutine immediately: we should lock only creating task, but not it's awaiting. Creating task itself wouldn't start that task (andl.acquire()
) immediately, we should return control to event loop. It can be done byawait asyncio.sleep(0)
. Here's final solution for original code:Output: