Issue
Suppose that the function takes_time is called every 0.1 seconds on the arguments 0, 1, 2, 3, ...
async def takes_time(i):
time.sleep(1)
After the first second, takes_time(0) will have been run once and takes_time(1), takes_time(2), ..., takes_time(9) are waiting to occur. I want to change this behavior so that those 9 calls are ignored, and takes_time only runs on the arguments 0, 10, 20, 30, etc. It does not matter to me whether they are off by 1 or 2 - I don't care about exactly which numbers are chosen - the main thing is that I want calls to takes_time to be ignored when another call is still running.
Here is my solution:
lock = asyncio.Lock()
async def takes_time(i):
if lock.locked():
return
async with lock:
time.sleep(1)
This solution works partially. It ensures that only one call to takes_time is running at a time, but it does not skip arguments 1 through 9 - even though the lock is locked when they are called. Why does this happen, and how can I fix it?
Solution
As mentioned in the comments, you should never use blocking calls like time.sleep()
in asyncio code. (The same applies for blocking network IO like requests
- there are asyncio libraries for that.) Since you don't need to wait for the lock, you probably don't need a full-blown lock in the first place, an ordinary boolean variable would suffice:
locked = False
async def takes_time(i):
global locked
if locked:
return
locked = True
try:
await asyncio.sleep(1)
finally:
locked = False
In a multi-threaded program, this kind of approach would be subject to a race condition because multiple threads could check locked
and find it false, both proceeding to set locked
to true and defeating exclusivity. In asyncio a task switch can occur only on await
(and additionally async for
and async with
). As the code between if locked
and locked = True
doesn't await
anything, it is executed atomically.
Answered By - user4815162342
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.