python 条件变量 `wait_for` 谓词未立即返回

python condition variable `wait_for`predicate not immediately returned

我正在尝试实现同步端点,其中将作业排队,等待作业完成,然后 return 结果。

lock = Condition()

def predicate(job_in_queue):
    job_in_queue.refresh()
    print(job_in_queue.get_status())
    print(datetime.datetime.now())
    if job_in_queue.get_status() == "finished":
        return True
    return False

print(datetime.datetime.now())
with lock:
    if lock.wait_for(lambda: predicate(job), timeout=10):
        print("indeed notified")
    else:
        print("failed to notify")
    print(datetime.datetime.now())
print(datetime.datetime.now())

return job.result

python条件变量的wait_for方法:wait_for(predicate, timeout=None),会停在这一行,等待callable传入,predicate,returns True,然后继续执行后面的代码。参考 the documentation.

但是,根据我的打印行,predicate 似乎没有经常被检查。当第一次调用 wait_for 时,它只在第一次通过 in/the 时检查一次,然后它处于空闲状态,仅在 t=timeout 秒后第二次检查,其中 timeout 是我传入的数字。在我的打印行中,它仅在 10 秒后检查 predicate(我在上面的代码中指定的 timeout 值)。

   2021-07-15T13:48:33.98+0800 [APP/PROC/WEB/0] OUT 2021-07-15 05:48:23.954320
   2021-07-15T13:48:33.98+0800 [APP/PROC/WEB/0] OUT queued
   2021-07-15T13:48:33.98+0800 [APP/PROC/WEB/0] OUT 2021-07-15 05:48:23.974196
   2021-07-15T13:48:33.98+0800 [APP/PROC/WEB/0] OUT finished
   2021-07-15T13:48:33.98+0800 [APP/PROC/WEB/0] OUT 2021-07-15 05:48:33.986337
   2021-07-15T13:48:33.98+0800 [APP/PROC/WEB/0] OUT indeed notified
   2021-07-15T13:48:33.98+0800 [APP/PROC/WEB/0] OUT 2021-07-15 05:48:33.987215
   2021-07-15T13:48:33.98+0800 [APP/PROC/WEB/0] OUT 2021-07-15 05:48:33.987233

我进一步验证了问题确实是 predicate 没有通过将 timeout 更改为 15 来不断检查,它再次只检查 15 之后的 predicate 结果秒。

有没有办法让 predicate 经常检查这里?不是 while True 忙等待,因为它会占用 CPU(并且永远不会通过代码审查)。或者 threading.Condition.wait_for 是去这里的正确方法吗?

Condition 对象的目的是同步两个或多个线程的activity。当 Thread-1 调用 Condition.wait 时,它就被阻塞了。因此,它不可能像您希望的那样检查谓词何时变为真。相反,它必须等待 Thread-2 做一些可能导致谓词变为真的事情。在那一刻,Thread-2 应该通知 Condition 对象。对 notify 的调用导致 Thread-1 唤醒并检查谓词。如果谓词现在为真,Thread-1 继续执行;否则它会再次阻塞。

文档中是这样说的:

》忽略超时特性,调用该方法大致等同于这样写:

while not predicate():
    cv.wait()

"

如果包含超时值,Thread-1 会在超时间隔后唤醒,而不管另一个线程是否调用了 notify。正如您观察到的那样,这会导致对谓词进行检查。

如果您想以合理的频率检查谓词,请提供足够短的超时时间或 确保另一个线程在适当的时间发出通知调用。

但是如果您不使用它来同步两个或多个线程,Condition 是错误的工具。如果你需要一个紧密的循环,写一个紧密的循环。