Python time.sleep () 대 event.wait ()
다중 스레드 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을 더 잘 사용합니까? 내 앱의 나머지 스레드가 더 반응하도록 만드는 것은 무엇입니까?
(일부 외부 이벤트 세트를 가정 exit
이나 exit_flag
, 및 종료하는 동안 전체 지연을 기다릴 기꺼이)
가 설정 exit_flag.wait(timeout=DELAY)
되면 즉시 while 루프에서 벗어날 수 있으므로 사용 하면 반응이 더 빨라집니다 exit_flag
. 를 사용 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
전체 DELAY
. 이제 GIL을 더 자주 획득하면 응용 프로그램의 다른 스레드에 눈에 띄는 영향이 있습니까? 어쩌면 아닐 수도 있습니다. 실행중인 다른 스레드의 수와 보유한 작업로드의 종류에 따라 다릅니다. 내 생각에는 많은 수의 스레드가 있거나 CPU 바운드 작업을 많이 수행하는 다른 스레드가 없으면 특별히 눈에 띄지 않을 것이지만 두 가지 방법을 모두 시도하고 볼 수있을만큼 쉽습니다.
Python 3.x에서는 구현의 대부분이 순수 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()
그리고 잠금을 획득하는 C 코드 :
/* 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;
}
이 구현은 응답 성이 뛰어나며 GIL을 다시 획득하는 빈번한 wakeup이 필요하지 않으므로 두 세계를 모두 활용할 수 있습니다.
파이썬 2. *
말했다 @dano처럼, event.wait 더 반응한다,
하지만 위험 할 수있는 시스템의 경우 시간이 뒤로 변경 , 그것의 기다리는 동안!
버그 # 1607041 : Condition.wait 시간 초과가 시계 변경에 실패 함
이 샘플을 참조하십시오.
def someHandler():
while not exit_flag.wait(timeout=0.100):
action()
일반적으로 action()
100ms intrvall에서 호출됩니다.
그러나 시간을 변경할 때. 한 시간 동안 두 작업 사이에 한 시간의 일시 중지가 있습니다.
결론 : 시간이 변경 될 수있는 경우에는 피해야합니다. event.wait
It is interesting to note that the event.wait() method can be invoked on its own:
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")
So why -not- use wait() as an alternative to sleep() outside of multi-threading? In a word, Zen. (Of course.) Clarity of code is an important thing.
참고URL : https://stackoverflow.com/questions/29082268/python-time-sleep-vs-event-wait
'Programing' 카테고리의 다른 글
교착 상태없이 동기식으로 메인 큐를 디스패치하는 방법은 무엇입니까? (0) | 2020.12.02 |
---|---|
Node.js 오류 : ECONNREFUSED 연결 (0) | 2020.12.02 |
블록 범위 변수 (타입 스크립트)를 재 선언 할 수 없습니다. (0) | 2020.12.02 |
Google Firestore-한 번의 왕복으로 여러 ID로 문서를 얻는 방법은 무엇입니까? (0) | 2020.12.02 |
OpenGL을 디버깅하는 가장 좋은 방법은 무엇입니까? (0) | 2020.12.02 |