如何使用 Python 中的 Event 对象再次启动线程?
How to start a thread again with an Event object in Python?
我想创建一个线程并用一个事件对象来控制它。详细地说,我希望线程在设置事件对象时执行,并重复等待。
下面是我想到的粗略逻辑。
import threading
import time
e = threading.Event()
def start_operation():
e.wait()
while e.is_set():
print('STARTING TASK')
e.clear()
t1 = threading.Thread(target=start_operation)
t1.start()
e.set() # first set
e.set() # second set
我希望 t1
到 运行 一旦 the first set
被命令并停止自己(由于它里面有 e.clear
),然后到 运行 在 the second set
被命令后再次。所以,按照我的预期,它应该打印出 'STARTING TASK' 两次。但它只显示一次,我不明白为什么。每当设置事件对象时,我应该如何更改代码以使其再次成为 运行 while loop
?
第一个问题是,一旦您退出 while
循环,您就已经退出了它。将谓词改回不会改变任何东西。暂时忘掉事件,看看这段代码:
i = 0
while i == 0:
i = 1
后面再设置i = 0
显然也无所谓吧?您已经离开了 while 循环和整个函数。而您的代码正在做完全相同的事情。
您可以通过添加另一个 while
循环来解决问题:
def start_operation():
while True:
e.wait()
while e.is_set():
print('STARTING TASK')
e.clear()
然而,这仍然行不通——除非偶尔偶然发生。
Event.set
不阻塞;它只是立即设置事件,即使它已经设置。所以,这里最可能的控制流程是:
- 后台线程命中
e.wait()
并阻塞。
- 主线程命中
e.set()
并设置事件。
- 主线程命中
e.set()
并再次设置事件,但没有任何效果。
- 后台线程唤醒,循环一次,最后调用
e.clear()
。
- 后台线程永远等待
e.wait()
。
(没有办法避免错过事件信号的事实实际上是发明条件的原因,并且比 Win32 和 Python 更新的任何东西都不会打扰事件......但是条件不是'这里也不够。)
如果你想让主线程一直阻塞到事件清除,然后才重新设置,那是不行的。你需要一些额外的东西,比如第二个事件,主线程可以等待,后台线程可以设置。
但是如果你想跟踪多个 set
调用而不遗漏任何一个,你需要使用不同的同步机制。 queue.Queue
在这里可能有点矫枉过正,但在 Python 中却非常简单,所以我们就使用它吧。当然,您实际上没有任何值可以放入队列中,但这没关系;你可以在那里粘贴一个虚拟值:
import queue
import threading
q = queue.Queue()
def start_operation():
while True:
_ = q.get()
print('STARTING TASK')
t1 = threading.Thread(target=start_operation)
t1.start()
q.put(None)
q.put(None)
如果您以后想添加一种关闭后台线程的方法,只需将其更改为坚持值:
import queue
import threading
q = queue.Queue()
def start_operation():
while True:
if q.get():
return
print('STARTING TASK')
t1 = threading.Thread(target=start_operation)
t1.start()
q.put(False)
q.put(False)
q.put(True)
我想创建一个线程并用一个事件对象来控制它。详细地说,我希望线程在设置事件对象时执行,并重复等待。
下面是我想到的粗略逻辑。
import threading
import time
e = threading.Event()
def start_operation():
e.wait()
while e.is_set():
print('STARTING TASK')
e.clear()
t1 = threading.Thread(target=start_operation)
t1.start()
e.set() # first set
e.set() # second set
我希望 t1
到 运行 一旦 the first set
被命令并停止自己(由于它里面有 e.clear
),然后到 运行 在 the second set
被命令后再次。所以,按照我的预期,它应该打印出 'STARTING TASK' 两次。但它只显示一次,我不明白为什么。每当设置事件对象时,我应该如何更改代码以使其再次成为 运行 while loop
?
第一个问题是,一旦您退出 while
循环,您就已经退出了它。将谓词改回不会改变任何东西。暂时忘掉事件,看看这段代码:
i = 0
while i == 0:
i = 1
后面再设置i = 0
显然也无所谓吧?您已经离开了 while 循环和整个函数。而您的代码正在做完全相同的事情。
您可以通过添加另一个 while
循环来解决问题:
def start_operation():
while True:
e.wait()
while e.is_set():
print('STARTING TASK')
e.clear()
然而,这仍然行不通——除非偶尔偶然发生。
Event.set
不阻塞;它只是立即设置事件,即使它已经设置。所以,这里最可能的控制流程是:
- 后台线程命中
e.wait()
并阻塞。 - 主线程命中
e.set()
并设置事件。 - 主线程命中
e.set()
并再次设置事件,但没有任何效果。 - 后台线程唤醒,循环一次,最后调用
e.clear()
。 - 后台线程永远等待
e.wait()
。
(没有办法避免错过事件信号的事实实际上是发明条件的原因,并且比 Win32 和 Python 更新的任何东西都不会打扰事件......但是条件不是'这里也不够。)
如果你想让主线程一直阻塞到事件清除,然后才重新设置,那是不行的。你需要一些额外的东西,比如第二个事件,主线程可以等待,后台线程可以设置。
但是如果你想跟踪多个 set
调用而不遗漏任何一个,你需要使用不同的同步机制。 queue.Queue
在这里可能有点矫枉过正,但在 Python 中却非常简单,所以我们就使用它吧。当然,您实际上没有任何值可以放入队列中,但这没关系;你可以在那里粘贴一个虚拟值:
import queue
import threading
q = queue.Queue()
def start_operation():
while True:
_ = q.get()
print('STARTING TASK')
t1 = threading.Thread(target=start_operation)
t1.start()
q.put(None)
q.put(None)
如果您以后想添加一种关闭后台线程的方法,只需将其更改为坚持值:
import queue
import threading
q = queue.Queue()
def start_operation():
while True:
if q.get():
return
print('STARTING TASK')
t1 = threading.Thread(target=start_operation)
t1.start()
q.put(False)
q.put(False)
q.put(True)