Python多线程:Event.set()会真正通知每个等待的线程吗?

24

如果我有一个 threading.Event,并且下面有两行代码:

event.set()
event.clear()

我有一些线程在等待该事件。

我的问题与调用set()方法时发生的情况有关:

  • 我能否绝对确定所有等待的线程都会被通知?(即,Event.set()“通知”线程)
  • 或者这两行在彼此之后非常快地执行,以至于某些线程仍然可能在等待吗?(即,Event.wait()轮询事件状态,该状态可能已经再次“清除”)

感谢您的答案!

3个回答

16
在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()返回的值是不可靠的。请参阅https://bugs.python.org/issue13502。 - David Röthlisberger

15

验证事物按预期工作很容易(注意:这是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.'

如果您运行上述脚本并且它终止,一切都应该很好 :-) 请注意,有100个线程正在等待事件被设置; 它会立即设置和清除; 所有线程都应该看到这一点并终止(虽然不是按任何确定的顺序,并且“全部完成”可能会在“按回车键”提示之后的任何位置打印,而不仅仅是在最后。

1

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接