使用exit_flag.wait(timeout=DELAY)
将具有更高的响应速度,因为在exit_flag
设置时,您将立即退出while循环。使用time.sleep
,即使在设置了事件之后,您也将在time.sleep
通话中等待直到睡了DELAY
几秒钟。
在实现方面,Python 2.x和Python 3.x具有非常不同的行为。在Python 2.xEvent.wait
中,使用大量的小time.sleep
调用在纯Python中实现:
from time import time as _time, sleep as _sleep
....
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:
if timeout is None:
waiter.acquire()
if __debug__:
self._note("%s.wait(): got it", self)
else:
endtime = _time() + timeout
delay = 0.0005
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
_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:
if timeout is None:
waiter.acquire()
gotit = True
else:
if timeout > 0:
gotit = waiter.acquire(True, timeout)
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()
和获得锁的C代码:
static PyLockStatus
acquire_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
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,因此您可以两全其美。
exit
标志的代码在哪里?是在action()
调用中还是在另一个线程中,或者可能是由信号处理程序调用的?