anında zaman while döngüsünün dışına kırarım, çünkü exit_flag
ayarlanmıştır. time.sleep
ile, etkinlik ayarlandıktan sonra bile, DELAY
saniye boyunca uyuyana kadar time.sleep
aramasında bekleyeceksiniz. Uygulama açısından, Python 2.x ve Python 3.x'in çok farklı davranışları vardır. olan bu aslında wait
kullanmak anlamına
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)
muhtemelen biraz CPU-aç sadece koşulsuz tam DELAY
uyuyan daha fazla, ama vardır: Python 2.x Event.wait
küçük time.sleep
aramaların bir demet kullanarak saf Python uygulanan fayda (DELAY
'un ne kadar uzun olduğuna bağlı olarak çok fazla) daha duyarlı. Ayrıca, GIL'in sıklıkla yeniden edinilmesi gerektiği anlamına gelir, böylece bir sonraki uyku planlanabilir, time.sleep
ise GIL'i tam DELAY
için serbest bırakabilir. Şimdi, GIL'i daha sık aldığınızda, uygulamanızdaki diğer iş parçacıkları üzerinde dikkat çekici bir etki var mı? Belki de belki de değil. Kaç tane iş parçacığının çalıştığına ve ne tür iş yüklerine sahip olduklarına bağlıdır. Tahminimce, çok sayıda iş parçacığına sahip olmadığınız sürece veya özellikle CPU'ya bağlı bir çok iş yapan başka bir iş parçacığınız olmadıkça, ancak her iki yolu da denemek ve görmek için yeterince kolay olmadıkça özellikle fark edilmeyecektir.
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()
Ve kilit edinme C kodu:
Python 3.x ise
, uygulama çok saf C kodu taşınır
/* Helper to acquire an interruptible lock with a timeout. If the lock acquire
* is interrupted, signal handlers are run, and if they raise an exception,
* PY_LOCK_INTR is returned. Otherwise, PY_LOCK_ACQUIRED or PY_LOCK_FAILURE
* are returned, depending on whether the lock can be acquired withing the
* timeout.
*/
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 {
/* first a simple non-blocking try without releasing the GIL */
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) {
/* Run signal handlers if we were interrupted. Propagate
* exceptions from signal handlers, such as KeyboardInterrupt, by
* passing up PY_LOCK_INTR. */
if (Py_MakePendingCalls() < 0) {
return PY_LOCK_INTR;
}
/* If we're using a timeout, recompute the timeout after processing
* signals, since those can take time. */
if (microseconds > 0) {
_PyTime_gettimeofday(&curtime);
microseconds = ((endtime.tv_sec - curtime.tv_sec) * 1000000 +
(endtime.tv_usec - curtime.tv_usec));
/* Check for negative values, since those mean block forever.
*/
if (microseconds <= 0) {
r = PY_LOCK_FAILURE;
}
}
}
} while (r == PY_LOCK_INTR); /* Retry if we were interrupted. */
return r;
}
Bu uygulama duyarlı ve GIL'i yeniden edinen sık rastlanan uyandırmalara gerek duymaz, böylece her iki dünyanın da en iyisini elde edersiniz.
kaçınmalıdır nerede 'exit' bayrağı ayarlar şifresi? 'Action() 'çağrısında mı yoksa başka bir iş parçacığında mı, yoksa bir sinyal işleyicisi tarafından mı çağrılıyor? – tdelaney
python 2.x arka planda yoklama olsa bile bu durumda 'Event.wait' kullanıyorum. Uykuda, 1 saniyelik aralıkların makul derecede duyarlı ve daha az müdahaleci olduğu söylenebilir. – tdelaney
Birincisi, bir şey için biraz CPU zamanı harcayacak. Event.wait'in – immibis