简体   繁体   English

Python 中是否有一种简单的方法可以等到某些条件成立?

[英]Is there an easy way in Python to wait until certain condition is true?

I need to wait in a script until a certain number of conditions become true?我需要在脚本中等待,直到一定数量的条件变为真?

I know I can roll my own eventing using condition variables and friends, but I don't want to go through all the trouble of implementing it, since some object property changes come from external thread in a wrapped C++ library (Boost.Python), so I can't just hijack __setattr__ in a class and put a condition variable there, which leaves me with either trying to create and signal a Python condition variable from C++, or wrap a native one and wait on it in Python, both of which sound fiddly, needlessly complicated and boring.我知道我可以使用条件变量和朋友来滚动我自己的事件,但我不想 go 通过实现它的所有麻烦,因为一些 object 属性更改来自包装的 ZF6F87C9FDCF8B3C3F07F93F1EE8 库中的外部线程,7 so I can't just hijack __setattr__ in a class and put a condition variable there, which leaves me with either trying to create and signal a Python condition variable from C++, or wrap a native one and wait on it in Python, both of which听起来很繁琐,不必要的复杂和无聊。

Is there an easier way to do it, barring continuous polling of the condition?有没有更简单的方法来做到这一点,除非连续轮询条件?

Ideally it would be along the lines of理想情况下,它将沿着

res = wait_until(lambda: some_predicate, timeout)
if (not res):
    print 'timed out'

Unfortunately the only possibility to meet your constraints is to periodically poll , eg...:不幸的是,满足您的约束的唯一可能性是定期轮询,例如...:

import time

def wait_until(somepredicate, timeout, period=0.25, *args, **kwargs):
  mustend = time.time() + timeout
  while time.time() < mustend:
    if somepredicate(*args, **kwargs): return True
    time.sleep(period)
  return False

or the like.之类的。 This can be optimized in several ways if somepredicate can be decomposed (eg if it's known to be an and of several clauses, especially if some of the clauses are in turn subject to optimization by being detectable via threading.Event s or whatever, etc, etc), but in the general terms you ask for, this inefficient approach is the only way out.如果可以分解somepredicate ,则可以通过多种方式对其进行优化(例如,如果已知它是一个and几个子句,尤其是如果某些子句又可以通过threading.Event或其他等检测到而进行优化,等等,等),但在您要求的一般条款中,这种低效的方法是唯一的出路。

Another nice package is waiting - https://pypi.org/project/waiting/另一个不错的包正在waiting - https://pypi.org/project/waiting/

install:安装:

pip install waiting

Usage: You pass a function that will be called every time as a condition, a timeout, and (this is useful) you can pass a description for the waiting, which will be displayed if you get TimeoutError.用法:你传递一个每次都会被调用的函数作为条件,一个超时,并且(这很有用)你可以传递一个等待的描述,如果你得到TimeoutError,它将显示出来。

using function:使用功能:

from waiting import wait


def is_something_ready(something):
    if something.ready():
        return True
    return False


# wait for something to be ready
something = # whatever

wait(lambda: is_something_ready(something), timeout_seconds=120, waiting_for="something to be ready")

# this code will only execute after "something" is ready
print("Done")

Note: the function must return a boolean - True when the wait is over, False otherwise注意:该函数必须返回一个布尔值 - 等待结束时返回 True,否则返回 False

Here is another solution.这是另一个解决方案。 The goal was to make threads to wait on each other before doing some work in a very precise order.目标是让线程在以非常精确的顺序执行某些工作之前相互等待。 The work can take unknown amount of time.这项工作可能需要未知的时间。 Constant polling is not good for two reasons: it eats CPU time and action does not start immediately after condition is met.持续轮询不好有两个原因:它会占用 CPU 时间,并且在满足条件后不会立即开始操作。

class Waiter():

    def __init__(self, init_value):
        self.var = init_value
        self.var_mutex = threading.Lock()
        self.var_event = threading.Event()

    def WaitUntil(self, v):
        while True:
            self.var_mutex.acquire()
            if self.var == v:
                self.var_mutex.release()
                return # Done waiting
            self.var_mutex.release()
            self.var_event.wait(1) # Wait 1 sec

    def Set(self, v):
        self.var_mutex.acquire()
        self.var = v
        self.var_mutex.release()
        self.var_event.set() # In case someone is waiting
        self.var_event.clear()

And the way to test it以及测试它的方法

class TestWaiter():

    def __init__(self):
        self.waiter = Waiter(0)
        threading.Thread(name='Thread0', target=self.Thread0).start()
        threading.Thread(name='Thread1', target=self.Thread1).start()
        threading.Thread(name='Thread2', target=self.Thread2).start()

    def Thread0(self):
        while True:
            self.waiter.WaitUntil(0)
            # Do some work
            time.sleep(np.random.rand()*2)
            self.waiter.Set(1)

    def Thread1(self):
        while True:
            self.waiter.WaitUntil(1)
            # Do some work
            time.sleep(np.random.rand())
            self.waiter.Set(2)

    def Thread2(self):
        while True:
            self.waiter.WaitUntil(2)
            # Do some work
            time.sleep(np.random.rand()/10)
            self.waiter.Set(0)

Waiter for multiprocessing:多处理服务员:

import multiprocessing as mp
import ctypes

class WaiterMP():
    def __init__(self, init_value, stop_value=-1):
        self.var = mp.Value(ctypes.c_int, init_value)
        self.stop_value = stop_value
        self.event = mp.Event()

    def Terminate(self):
        self.Set(self.stop_value)

    def Restart(self):
        self.var.value = self.init_value

    def WaitUntil(self, v):
        while True:
            if self.var.value == v or self.var.value == self.stop_value:
                return
            # Wait 1 sec and check aiagn (in case event was missed)
            self.event.wait(1)

    def Set(self, v):
        exit = self.var.value == self.stop_value
        if not exit: # Do not set var if threads are exiting
            self.var.value = v
        self.event.set() # In case someone is waiting
        self.event.clear()

Please comment if this is still not the best solution.如果这仍然不是最佳解决方案,请发表评论。

You've basically answered your own question: no.你基本上已经回答了你自己的问题:不。

Since you're dealing with external libraries in boost.python, which may change objects at their leisure, you need to either have those routines call an event handler refresh, or work with a condition.由于您正在处理 boost.python 中的外部库,它可能会在闲暇时更改对象,因此您需要让这些例程调用事件处理程序刷新,或者使用条件。

Here is the threading extention to Alex's solution:这是 Alex 解决方案的线程扩展:

import time
import threading

# based on https://stackoverflow.com/a/2785908/1056345                                                                                                                                                                                                                                                                         
def wait_until(somepredicate, timeout, period=0.25, *args, **kwargs):
    must_end = time.time() + timeout
    while time.time() < must_end:
        if somepredicate(*args, **kwargs):
            return True
        time.sleep(period)
    return False

def wait_until_par(*args, **kwargs):
    t = threading.Thread(target=wait_until, args=args, kwargs=kwargs)
    t.start()
    print ('wait_until_par exits, thread runs in background')

def test():
    print('test')

wait_until_par(test, 5)

From the computational perspective there must be a check for all conditions somewhere, sometime.从计算的角度来看,必须在某时某处检查所有条件。 If you have two parts of code, one that generates conditions changes and the other one that should be executed when some are true, you can do the following:如果您有两部分代码,一部分生成条件更改,另一部分应在某些条件为真时执行,您可以执行以下操作:

Have the code that changes conditions in, say, main thread, and the code that should be launched when some conditions are true, in a worker thread.在工作线程中拥有更改条件的代码,例如主线程,以及在某些条件为真时应该启动的代码。

from threading import Thread,Event

locker = Event()

def WhenSomeTrue(locker):
    locker.clear() # To prevent looping, see manual, link below
    locker.wait(2.0) # Suspend the thread until woken up, or 2s timeout is reached
    if not locker.is_set(): # when is_set() false, means timeout was reached
        print('TIMEOUT')
    else:
    #
    # Code when some conditions are true
    #
worker_thread = Thread(target=WhenSomeTrue, args=(locker,))
worker_thread.start()

cond1 = False
cond2 = False
cond3 = False

def evaluate():
    true_conditions = 0

    for i in range(1,4):
        if globals()["cond"+str(i)]: #access a global condition variable one by one
            true_conditions += 1     #increment at each true value
    if true_conditions > 1:
        locker.set() # Resume the worker thread executing the else branch
    #Or just if true_conditions > 1: locker.set();
    #true_conditions would need be incremented when 'True' is written to any of those variables

#
# some condition change code
#
evaluate()

For more information concerning this method, visit: https://docs.python.org/3/library/threading.html#event-objects有关此方法的更多信息,请访问: https ://docs.python.org/3/library/threading.html#event-objects

Proposed solution:建议的解决方案:

def wait_until(delegate, timeout: int):
    end = time.time() + timeout
    while time.time() < end:
        if delegate():
            return True
        else:
            time.sleep(0.1)
    return False

Usage:用法:

wait_until(lambda: True, 2)

In 2022 now you could use https://trio-util.readthedocs.io/en/latest/#trio_util.AsyncValue在 2022 年,您现在可以使用https://trio-util.readthedocs.io/en/latest/#trio_util.AsyncValue

I think this comes closest to what you want in its "smoothest" form我认为这最接近你想要的“最流畅”的形式

I once used this in my code:我曾经在我的代码中使用过这个:

while not condition:
    pass

Hope this helps希望这可以帮助

This worked for me这对我有用

direction = ''
t = 0
while direction == '' and t <= 1:
    sleep(0.1)
    t += 0.1

This is for waiting for a signal while making sure time limit of 1 second这是为了等待信号,同时确保 1 秒的时间限制

here's how:就是这样:

import time

i = false

while i == false:
    if (condition):
        i = true
        break

Here's my Code I used during one of my Projects :这是我在一个项目中使用的代码:

import time
def no() :
    if (Condition !!!) :
        it got true
        oh()
    else:
        time.sleep(1) /Don't remove or don't blame me if ur system gets ""DEAD""
        no()
def oh() :   /Ur main program 
    while True:
        if(bla) :
            .......
            no()
        else :
            time.sleep(1)
            oh()

oh()

Hope it Helps希望能帮助到你

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM