Python threading: будет Event.set() действительно уведомлять каждую ожидающую нить

Если у меня есть threading.Event и следующие две строки кода...

event.set()
event.clear()

... и у меня есть некоторые темы, которые ждут этого события.

Мой вопрос связан с тем, что происходит при вызове метода set():

  • Могу ли я быть АБСОЛЮТНО уверенным, что все ожидающие потоки будут уведомлены? (т.е. Event.set() "уведомляет" потоки)
  • Или может случиться так, что эти две строки выполняются так быстро после друг друга, что некоторые потоки все еще могут ждать? (т.е. Event.wait() проверяет состояние события, которое может быть уже "очищено" ).

Спасибо за ваши ответы!

Ответ 1

Достаточно легко проверить, что все работает так, как ожидалось:

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.'

Если вы запустите вышеуказанный script, и он завершается, все должно быть хорошо:-) Обратите внимание, что сто потоков ждут, когда событие будет установлено; он установил и очистил сразу; все потоки должны видеть это и должны заканчиваться (хотя и не в каком-либо определенном порядке, а "Все сделано" может быть напечатано в любом месте после приглашения "Нажмите ввод", а не только в самом конце.

Ответ 2

Внутри 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)