#python #multithreading #events #wait
#python #многопоточность #Мероприятия #подождите
Вопрос:
Если у меня есть threading.Event
и следующие две строки кода:
event.set()
event.clear()
и у меня есть несколько потоков, которые ожидают этого события.
Мой вопрос связан с тем, что происходит при вызове set()
метода:
- Могу ли я быть АБСОЛЮТНО уверен, что все ожидающие потоки будут уведомлены? (т. е.
Event.set()
«уведомляет» потоки) - Или может случиться так, что эти две строки выполняются так быстро друг за другом, что некоторые потоки могут все еще ожидать? (т. е.
Event.wait()
опрашивает состояние события, которое, возможно, уже снова «очищено»)
Спасибо за ваши ответы!
Ответ №1:
Во внутренних компонентах Python событие реализуется с Condition()
объектом.
При вызове event.set()
метода вызывается notify_all()
условие (после получения блокировки, чтобы убедиться, что она не прерывается), затем все потоки получают уведомление (блокировка снимается только тогда, когда все потоки уведомлены), поэтому вы можете быть уверены, что все потоки будут эффективно уведомлены.
Теперь очистка события сразу после уведомления не является проблемой …. пока вы не захотите проверять значение события в ожидающих потоках с помощью event.is_set()
, но вам нужна такая проверка только в том случае, если вы ожидали с таймаутом.
Примеры :
псевдокод, который работает :
#in main thread
event = Event()
thread1(event)
thread2(event)
...
event.set()
event.clear()
#in thread code
...
event.wait()
#do the stuff
псевдокод, который может не работать :
#in main thread
event = Event()
thread1(event)
thread2(event)
...
event.set()
event.clear()
#in thread code
...
while not event.is_set():
event.wait(timeout_value)
#do the stuff
Отредактировано: в python > = 2.7 вы все еще можете дождаться события с таймаутом и быть уверенным в состоянии события :
event_state = event.wait(timeout)
while not event_state:
event_state = event.wait(timeout)
Комментарии:
1. Обратите внимание, что в Python 2.7 возвращаемое значение из
event.wait()
ненадежно, если вы указываете тайм-аут. Смотрите bugs.python.org/issue13502
Ответ №2:
Достаточно легко убедиться, что все работает так, как ожидалось (Примечание: это код Python 2, который потребуется адаптировать для Python 3):
import threading
e = threading.Event()
threads = []
def runner():
tname = threading.current_thread().name
print 'Thread waiting for event: %s' % tname
e.wait()
print 'Thread got event: %s' % tname
for t in range(100):
t = threading.Thread(target=runner)
threads.append(t)
t.start()
raw_input('Press enter to set and clear the event:')
e.set()
e.clear()
for t in threads:
t.join()
print 'All done.'
Если вы запустите приведенный выше скрипт и он завершится, все должно быть хорошо 🙂 Обратите внимание, что сотня потоков ожидают установки события; оно сразу устанавливается и очищается; все потоки должны увидеть это и должны завершиться (хотя и не в каком-либо определенном порядке, и «Все готово» может быть напечатано в любом месте после приглашения «Нажать enter», а не только в самом конце.
Ответ №3:
Python 3
Проще проверить, что это работает
import threading
import time
lock = threading.Lock() # just to sync printing
e = threading.Event()
threads = []
def runner():
tname = threading.current_thread().name
with lock:
print('Thread waiting for event ', tname)
e.wait()
with lock:
print('Thread got event: ', tname)
for t in range(8): # Create 8 threads could be 100's
t = threading.Thread(target=runner)
threads.append(t)
t.start()
time.sleep(1) # force wait until set/clear
e.set()
e.clear()
for t in threads:
t.join()
print('Done')