Python time.sleep()vs event.wait()

时间:2015-03-16 16:40:06

标签: python multithreading sleep

我想在我的多线程Python应用程序中定期执行操作。我已经看到了两种不同的方法

exit = False
def thread_func(): 
    while not exit:
       action()
       time.sleep(DELAY)

exit_flag = threading.Event()
def thread_func(): 
    while not exit_flag.wait(timeout=DELAY):
       action()

单向另一种方式有优势吗?是否使用更少的资源,或者与其他线程和GIL玩得更好?哪一个使我的应用程序中的剩余线程更具响应性?

(假设一些外部事件集exitexit_flag,我愿意在关闭时等待完整的延迟)

4 个答案:

答案 0 :(得分:60)

使用exit_flag.wait(timeout=DELAY)会更具响应性,因为在设置exit_flag后,您将立即突破while循环。使用time.sleep,即使在事件发生后,您也会在time.sleep电话中等待,直到您睡眠DELAY秒为止。

在实现方面,Python 2.x和Python 3.x具有非常不同的行为。在Python 2.x Event.wait中使用一堆小time.sleep调用在纯Python中实现:

from time import time as _time, sleep as _sleep

....
# This is inside the Condition class (Event.wait calls Condition.wait).
def wait(self, timeout=None):
    if not self._is_owned():
        raise RuntimeError("cannot wait on un-acquired lock")
    waiter = _allocate_lock()
    waiter.acquire()
    self.__waiters.append(waiter)
    saved_state = self._release_save()
    try:    # restore state no matter what (e.g., KeyboardInterrupt)
        if timeout is None:
            waiter.acquire()
            if __debug__:
                self._note("%s.wait(): got it", self)
        else:
            # Balancing act:  We can't afford a pure busy loop, so we
            # have to sleep; but if we sleep the whole timeout time,
            # we'll be unresponsive.  The scheme here sleeps very
            # little at first, longer as time goes on, but never longer
            # than 20 times per second (or the timeout time remaining).
            endtime = _time() + timeout
            delay = 0.0005 # 500 us -> initial delay of 1 ms
            while True:
                gotit = waiter.acquire(0)
                if gotit:
                    break
                remaining = endtime - _time()
                if remaining <= 0:
                    break
                delay = min(delay * 2, remaining, .05)
                _sleep(delay)
            if not gotit:
                if __debug__:
                    self._note("%s.wait(%s): timed out", self, timeout)
                try:
                    self.__waiters.remove(waiter)
                except ValueError:
                    pass
            else:
                if __debug__:
                    self._note("%s.wait(%s): got it", self, timeout)
    finally:
        self._acquire_restore(saved_state)

这实际上意味着使用wait可能比仅仅无条件地使用DELAY睡眠更耗费CPU,但是有好处(可能很多,取决于{{1}多长时间}更具响应性。这也意味着需要经常重新获取GIL,以便可以安排下一次睡眠,而DELAY可以释放完整time.sleep的GIL。现在,更频繁地获取GIL会对应用程序中的其他线程产生明显影响吗?也许或许没有。这取决于正在运行的其他线程数以及它们具有哪种工作负载。我的猜测是,除非你拥有大量的线程,或者可能是另一个执行大量CPU限制工作的线程,否则它会特别引人注目,但它很容易通过两种方式尝试并看到它。

在Python 3.x中,大部分实现都转移到了纯C代码:

DELAY

获取锁定的C代码:

import _thread # C-module
_allocate_lock = _thread.allocate_lock

class Condition:
    ...
    def wait(self, timeout=None):
        if not self._is_owned():
            raise RuntimeError("cannot wait on un-acquired lock")
        waiter = _allocate_lock()
        waiter.acquire()
        self._waiters.append(waiter)
        saved_state = self._release_save()
        gotit = False
        try:    # restore state no matter what (e.g., KeyboardInterrupt)
            if timeout is None:
                waiter.acquire()
                gotit = True
            else:
                if timeout > 0:
                    gotit = waiter.acquire(True, timeout)  # This calls C code
                else:
                    gotit = waiter.acquire(False)
            return gotit
        finally:
            self._acquire_restore(saved_state)
            if not gotit:
                try:
                    self._waiters.remove(waiter)
                except ValueError:
                    pass

class Event:
    def __init__(self):
        self._cond = Condition(Lock())
        self._flag = False

    def wait(self, timeout=None):
        self._cond.acquire()
        try:
            signaled = self._flag
            if not signaled:
                signaled = self._cond.wait(timeout)
            return signaled
        finally:
            self._cond.release()

此实现具有响应性,并且不需要频繁唤醒重新获取GIL,因此您可以充分利用这两个世界。

答案 1 :(得分:6)

Python 2. *
就像@dano所说的那样,event.wait响应更快,
当系统时间向后改变时,可能是危险的,而它正在等待!
bug# 1607041: Condition.wait timeout fails on clock change

请参阅此示例:

def someHandler():
   while not exit_flag.wait(timeout=0.100):
       action()

通常情况下,action()将在100毫秒的内部呼叫中被调用 但是当你改变时间时。一小时后,两个动作之间暂停一小时。

结论:如果允许时间可以更改,则应避免event.wait

答案 2 :(得分:2)

有趣的是,可以单独调用event.wait()方法:

from threading import Event # Needed for the  wait() method
from time import sleep     

print("\n Live long and prosper!")
sleep(1)               # Conventional sleep() Method.
print("\n Just let that soak in..")   
Event().wait(3.0) # wait() Method, useable sans thread.
print("\n Make it So! = )\n")

那么,为什么在多线程之外不使用wait()替代sleep()?一句话,禅。 (当然。)代码清晰是很重要的。

答案 3 :(得分:0)

根据我的经验,使用 time.sleep() 会消耗 CPU 并使应用程序滞后,这是因为 sleep 函数是其他线程的阻塞方法,而 Event.wait() 是非阻塞方法其他线程。

您可以通过查看线程等待释放锁所需的时间来了解这一点!

此外,如果您不知道阻塞线程所需的时间,Event.wait() 非常有用!这样你就可以setclear事件