使用
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
,但这样做的好处是(
DELAY响应时间长很多,取决于时间长短)。这也意味着需要经常重新获取GIL,以便可以安排下一次睡眠,同时
time.sleep可以完全释放GIL
DELAY。现在,更频繁地获取GIL是否会对您应用程序中的其他线程产生明显影响?也许不是。这取决于正在运行的其他线程数以及它们承担的工作量。我的猜测是,除非您有大量线程,或者另一个线程正在执行大量CPU限制工作,否则它不会特别引人注目,但是它很容易同时尝试和观察。
在Python 3.x中,大部分实现都移至纯C代码:
import _thread # C-module_allocate_lock = _thread.allocate_lockclass 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 pre else: gotit = waiter.acquire(False) return gotit finally: self._acquire_restore(saved_state) if not gotit: try: self._waiters.remove(waiter) except ValueError: passclass 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()
和获得锁的C代码:
static PyLockStatusacquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds){ PyLockStatus r; _PyTime_timeval curtime; _PyTime_timeval endtime; if (microseconds > 0) { _PyTime_gettimeofday(&endtime); endtime.tv_sec += microseconds / (1000 * 1000); endtime.tv_usec += microseconds % (1000 * 1000); } do { r = PyThread_acquire_lock_timed(lock, 0, 0); if (r == PY_LOCK_FAILURE && microseconds != 0) { Py_BEGIN_ALLOW_THREADS // GIL is released here r = PyThread_acquire_lock_timed(lock, microseconds, 1); Py_END_ALLOW_THREADS } if (r == PY_LOCK_INTR) { if (Py_MakePendingCalls() < 0) { return PY_LOCK_INTR; } if (microseconds > 0) { _PyTime_gettimeofday(&curtime); microseconds = ((endtime.tv_sec - curtime.tv_sec) * 1000000 + (endtime.tv_usec - curtime.tv_usec)); if (microseconds <= 0) { r = PY_LOCK_FAILURE; } } } } while (r == PY_LOCK_INTR); return r;}
该实现是响应式的,不需要频繁唤醒就可以重新获取GIL,因此您可以两全其美。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)