代码之家  ›  专栏  ›  技术社区  ›  Alex B

在Python中有没有一种简单的方法来等待某个条件为真?

  •  22
  • Alex B  · 技术社区  · 16 年前

    我知道我可以使用条件变量和朋友来滚动我自己的事件,但是我不想经历所有的麻烦,因为一些对象属性的变化来自包裹的C++库中的外部线程(Boosix.python),所以我不能只是劫持。 __setattr__ 在一个类中,并将一个条件变量放在那里,这就让我尝试从C++中创建和发出Python条件变量,或者包装一个本地变量,并在Python上等待,这两个都听起来很不必要,复杂且乏味。

    理想情况下,它将沿着

    res = wait_until(lambda: some_predicate, timeout)
    if (not res):
        print 'timed out'
    
    4 回复  |  直到 16 年前
        1
  •  40
  •   Mariusz Jamro    11 年前

    不幸的是,满足您的约束的唯一可能性是定期 ,例如……:

    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
    

    somepredicate 可以分解(例如,如果已知是 and threading.Event 但一般来说,这种低效的方法是唯一的出路。

        2
  •  9
  •   Alon Gouldman    5 年前

    另一个不错的包裹是 waiting - https://pypi.org/project/waiting/

    安装:

    pip install waiting
    

    用法: 您可以传递一个函数,该函数将在每次调用时作为一个条件、一个超时,并且(这很有用)您可以传递等待的描述,如果您得到TimeoutError,将显示该描述。

    使用函数:

    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")
    
    

    注意:等待结束时,函数必须返回布尔值-True,否则返回False

        3
  •  4
  •   Alex    6 年前

    这是另一个解决方案。目标是让线程在以非常精确的顺序执行某些工作之前互相等待。这项工作可能需要不知多少时间。持续轮询不好有两个原因:它占用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()
    

    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)
    

    多处理服务生:

    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()
    

        4
  •  3
  •   Yann Ramin    16 年前

    由于您在boost.python中处理外部库,这些库可能会在空闲时更改对象,因此您需要让这些例程调用事件处理程序刷新,或者处理一个条件。

        5
  •  3
  •   Can Kavaklıoğlu    9 年前

    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)
    
        6
  •  0
  •   Roger Hoem-Martinsen    7 年前

    建议解决方案:

    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
    

    用法:

    wait_until(lambda: True, 2)
    
        7
  •  0
  •   Patrik Staron    5 年前

    在主线程中设置更改条件的代码,在工作线程中设置当某些条件为真时应启动的代码。

    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()
    

    https://docs.python.org/3/library/threading.html#event-objects

        8
  •  -1
  •   Yamaneko    5 年前

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

    这是为了等待信号,同时确保时间限制为1秒

        9
  •  -4
  •   Aryaveer Singh Rawat    6 年前

    以下是我在一个项目中使用的代码:

    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()
    

    希望有帮助