Python 线程:Event.set() 真的会通知所有等待线程吗

24 投票
3 回答
40873 浏览
提问于 2025-04-16 19:02

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

event.set()
event.clear()

而且我有一些线程在等待这个事件。

我想问的是,当调用 set() 方法时,会发生什么:

  • 我能绝对确定所有等待的线程都会被通知吗?(也就是说,Event.set() 会“通知”这些线程)
  • 还是说可能会发生这两行代码执行得太快,以至于有些线程仍然在等待?(也就是说,Event.wait() 会检查事件的状态,而这个状态可能已经被“清除”了)

谢谢你的回答!

3 个回答

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

如果你运行上面的脚本并且它正常结束,那就说明一切都没问题 :-) 注意,有一百个线程在等待事件被触发;这个事件被触发后又马上被清除;所有线程应该都能看到这个变化并结束运行(不过结束的顺序可能不固定,"All done"这句可以在"Press enter"提示后面的任何地方打印出来,而不仅仅是在最后)。

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)

撰写回答