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

Posted

技术标签:

【中文标题】python条件变量`wait_for`谓词没有立即返回【英文标题】:python condition variable `wait_for`predicate not immediately returned 【发布时间】:2021-09-24 01:27:09 【问题描述】:

我正在尝试实现一个同步端点,将作业排入队列,等待作业完成,然后返回结果。

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,返回True,然后继续后面的代码。参考the documentation。

但是,根据我的打印行,predicate 似乎没有被经常检查。它只在第一次传入/第一次调用wait_for 时检查一次,然后它统计空闲并且只在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 是正确的方式吗?

【问题讨论】:

【参考方案1】:

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

在文档中它说:

"忽略超时特性,调用这个方法大致相当于写:

while not predicate():
    cv.wait()

"

如果您包含超时值,则 Thread-1 在超时间隔后唤醒,而不管其他线程是否已调用 notify。正如您所观察到的,这会导致检查谓词。

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

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

【讨论】:

以上是关于python条件变量`wait_for`谓词没有立即返回的主要内容,如果未能解决你的问题,请参考以下文章

std::future::wait_for 虚假唤醒?

为啥“等待谓词”解决了条件变量的“丢失唤醒”?

C++ std::condition_variable wait() wait_for() 区别 怎么用 实例

Python3 asyncio:wait_for()通信()超时,如何获得部分结果?

Python asyncio condition 中 wait_for 方法的简单使用

有没有办法在 Java 流上使用条件谓词?