如果锁定,则不要运行异步函数 - Python



假设函数takes_time每0.1秒对参数0、1、2、3…调用一次。。。

async def takes_time(i):
time.sleep(1)

在第一秒之后,takes_time(0(将运行一次,takes_time(1(,takes\utime(2(。。。,takes_time(9(正在等待发生。我想改变这种行为,使这9个调用被忽略,take_time只在参数0、10、20、30等上运行。对我来说,它们是偏离1还是偏离2并不重要——我不在乎到底选择了哪些数字——最重要的是,当另一个调用仍在运行时,我希望take_time的调用被忽略。

这是我的解决方案:

lock = asyncio.Lock()
async def takes_time(i):
if lock.locked():
return
async with lock:
time.sleep(1)

此解决方案部分有效。它确保一次只运行一个对takes_time的调用,但它不会跳过参数1到9,即使在调用它们时锁是锁定的。为什么会发生这种情况,我该如何解决?

正如注释中所提到的,您永远不应该在异步代码中使用像time.sleep()这样的阻塞调用。(这同样适用于像requests这样的阻塞网络IO-有异步库可以实现这一点。(由于您不需要等待锁,因此您可能首先不需要一个完整的锁,一个普通的布尔变量就足够了:

locked = False
async def takes_time(i):
global locked
if locked:
return
locked = True
try:
await asyncio.sleep(1)
finally:
locked = False

在多线程程序中,这种方法会受到竞争条件的影响,因为多个线程可能会检查locked并发现它为假,从而将locked设置为真并破坏独占性。在异步中,任务切换只能发生在await上(另外还有async forasync with(。由于if lockedlocked = True之间的代码对await没有任何作用,因此它是原子执行的。

最新更新