Issue
Can we somehow create something similar using asyncio to how Redis named lock behaves?
In Redis we can use lock with key, in that way if you change the key it will change the lock.
That way we will be able to use different locks for different keys which will speed up things.
with r.lock('my_lock'):
# ...
Can we do something similar using asyncio
? Thx!
Solution
With a functional approach and a dict you could do something like this:
import asyncio
from contextlib import asynccontextmanager # python 3.7+
from random import randint
locks = {}
@asynccontextmanager
async def get_lock(id):
if not locks.get(id):
locks[id] = asyncio.Lock()
async with locks[id]:
yield
if len(locks[id]._waiters) == 0 and is_obsolete(id):
del locks[id]
print(len(locks))
def is_obsolete(id):
return True # whatever test you need
async def main():
tasks = []
for _ in range(100):
id = randint(0, 10)
tasks.append(asyncio.create_task(do_task(id)))
await asyncio.sleep(0.01)
await asyncio.gather(*[task for task in tasks if not task.done()])
async def do_task(id):
async with get_lock(id):
await asyncio.sleep(randint(0, 10) / 10)
asyncio.run(main())
The locks get removed as soon as there are no waiting tasks left and maybe another criterium based on your application (like if the resource still exists).
This should work for any hashable id
. Great with pathlib.Path
for example.
I don't know how efficient it needs to be for you. The nested context manager might be a bummer. I guess the alternative would be to subclass asyncio.Lock
to override __aexit__
and make some sort of manager class but I think this would be more verbose.
Answered By - thisisalsomypassword
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.