为什么 “event.set()” 不会破坏子线程中的 “while not event.is_set()” 循环?(见代码)

Why does "event.set()" not break a "while not event.is_set()" loop in a sub-thread? (see code)

提问人:Qosa 提问时间:8/5/2022 更新时间:11/28/2022 访问量:931

问:

我正在使用事件在我的主代码和子线程之间进行通信。线程包含一个 while 循环,一旦在主代码中调用 stop_event.set(),该循环就应该退出:

import time
from threading import *


def stream_data(stop_event, new_image_event):

    while not stop_event.is_set():
        new_image_event.wait()
        new_image_event.clear()

        # Do some stuff
        print('Stuff!')

    print('Successfully exited while loop!')
    return 


if __name__ == '__main__':

    # Initialising events
    stop_event = Event()
    new_image_event = Event()

    # Starting the thread
    thread = Thread(target=stream_data, args=(stop_event, new_image_event, ))
    thread.start()

    # Do stuff before setting the stop event, while generating a 'new' new_image_event every 1s
    for i in range(5):
        new_image_event.set()
        time.sleep(1)

    # generating the stop event
    stop_event.set()
    print('stop_event has been set')

    # joining back the thread
    thread.join()

输出:

Stuff!
Stuff!
Stuff!
Stuff!
Stuff!
stop_event has been set

因此,new_image_event完成了它的工作,最后成功设置了stop_event。但是由于某种原因,这不会中断线程中的while循环,并且永远不会打印“Successfully exited while loop!”。为什么?我该如何解决这个问题(最好不求助于类和/或自我)?

多线程 事件 while-loop 事件处理 python-multithreading

评论


答:

0赞 Paul Cornelius 8/10/2022 #1

该函数stream_data几乎所有时间都在等待new_image_event,如下所示:

new_image_event.wait()

当您的主线程设置new_image_event时,您将主线程休眠 1 秒钟。这有足够的时间让stream_data醒来,运行 print 语句,返回到循环的顶部,然后再次等待new_image_event。

当你的主线程设置stop_event时,函数stream_data当然在循环中,等待new_image_event。它无法返回到您检查stop_event的循环顶部。没有办法退出循环。

试试这个:

def stream_data(stop_event, new_image_event):
    while True:
        new_image_event.wait()
        new_image_event.clear()
        if stop_event.is_set():
            break
        
        # Do some stuff
        print('Stuff!')

    print('Successfully exited while loop!')

评论

1赞 Basil 11/28/2022
我 100% 同意您的分析,但您的解决方案仍然没有达到目标。没有确定性的保证,将紧随其后设置。事实上,当我运行这个解决方案时,线程仍然在等待中挂起,而从未加入主线程。stop_eventnew_image_event.clear()
1赞 Basil 11/28/2022 #2

您试图满足等待,同时保持响应。正如保罗在诊断你的问题时正确地指出的那样,当你处于另一个事件中时,你不能对这个事件做出回应。然而,他的解决方案是错误的。new_image_eventstop_eventstop_eventwait

我认为您最好的解决方案是利用 的超时机制。根据文档Event.wait()

当且仅当内部标志已设置为 true 时,此方法返回 True,无论是在等待调用之前还是等待开始之后,因此它将始终返回 True,除非给定超时且操作超时。

换言之,将线程循环更改为以下内容:

def stream_data(stop_event, new_image_event):
    while not stop_event.is_set():
        # Adjust the following timeout as appropriate. It controls
        # the amount of "dead time" before this thread responds
        # to `stop_event` if `new_image_event` was not raised
        # during the timeout.
        if new_image_event.wait(0.1):
            new_image_event.clear()
            # Do some stuff
            print('Stuff!')
        time.sleep(0.001)  # Breathe, dawg

    print('Successfully exited while loop!')
    return

这将产生您期望的结果:

Stuff!
Stuff!
Stuff!
Stuff!
Stuff!
stop_event has been set
Successfully exited while loop!

p.s. 循环底部的 1 毫秒睡眠并不是绝对必要的,但我发现它通常很有用,这样线程就有机会“呼吸”,而不是不停地追逐自己的尾巴:)