【问题标题】:Is there an easy way in Python to wait until certain condition is true?Python中是否有一种简单的方法可以等到某些条件成立?
【发布时间】:2022-02-16 09:06:00
【问题描述】:

我需要在脚本中等待,直到一定数量的条件变为真?

我知道我可以使用条件变量和朋友来滚动自己的事件,但我不想经历实现它的所有麻烦,因为一些对象属性更改来自包装 C++ 库 (Boost. Python),所以我不能只在一个类中劫持__setattr__ 并在那里放置一个条件变量,这让我要么尝试从 C++ 创建和发送 Python 条件变量,要么包装一个原生变量并等待它Python,这两种语言听起来都很繁琐、不必要的复杂和无聊。

有没有更简单的方法来做到这一点,除非连续轮询条件?

理想情况下是这样的

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

【问题讨论】:

    标签: python


    【解决方案1】:

    不幸的是,满足您的限制的唯一可能性是定期投票,例如....:

    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.Events 或等等等等),但就您所要求的一般术语而言,这种低效的方法是唯一的出路。

    【讨论】:

    【解决方案2】:

    另一个不错的包是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

    【讨论】:

    • 等待函数应该使用 pass timeout_seconds 代替 timeout
    【解决方案3】:

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

    如果这仍然不是最佳解决方案,请发表评论。

    【讨论】:

    • 谢谢。我不知道事件,set()wait()。最终在我的代码中使用了这种类型的模式。绝对比sleep()优雅得多
    • 看起来很有希望!比其他涉及睡眠的解决方案要好得多。据我所知,测试变量不需要获取锁,只需要修改它
    【解决方案4】:

    您基本上已经回答了自己的问题:不。

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

    【讨论】:

      【解决方案5】:

      这是 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)
      

      【讨论】:

        【解决方案6】:

        从计算的角度来看,必须在某时某处检查所有条件。如果您有两部分代码,一部分生成条件更改,另一部分应在某些条件为真时执行,您可以执行以下操作:

        在工作线程中包含更改条件的代码,例如主线程,以及在某些条件为真时应启动的代码。

        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

        【讨论】:

          【解决方案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)
          

          【讨论】:

            【解决方案8】:

            这对我有用

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

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

            【讨论】:

              【解决方案9】:

              方法如下:

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

              【讨论】:

              • 为什么要包含时间模块?当break语句足以让你退出循环时,为什么要设置i=True
              【解决方案10】:

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

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

              希望对你有帮助

              【讨论】:

                猜你喜欢
                • 2013-07-29
                • 2011-10-17
                • 2010-12-01
                • 2011-11-03
                • 1970-01-01
                • 1970-01-01
                • 2012-07-08
                • 2020-03-18
                • 2011-01-29
                相关资源
                最近更新 更多