What's the proper way to tell a looping thread to stop looping?
告诉循环线程停止循环的正确方法是什么?
I have a fairly simple program that pings a specified host in a separate threading.Thread
class. In this class it sleeps 60 seconds, the runs again until the application quits.
我有一个相当简单的程序,它在一个单独的threading.Thread类中ping指定的主机。在这个类中,它会休眠60秒,再次运行直到应用程序退出。
I'd like to implement a 'Stop' button in my wx.Frame
to ask the looping thread to stop. It doesn't need to end the thread right away, it can just stop looping once it wakes up.
我想在我的wx.Frame中实现一个“停止”按钮,要求循环线程停止。它不需要立即结束线程,它可以在唤醒后停止循环。
Here is my threading
class (note: I haven't implemented looping yet, but it would likely fall under the run method in PingAssets)
这是我的线程类(注意:我还没有实现循环,但它可能属于PingAssets中的run方法)
class PingAssets(threading.Thread):
def __init__(self, threadNum, asset, window):
threading.Thread.__init__(self)
self.threadNum = threadNum
self.window = window
self.asset = asset
def run(self):
config = controller.getConfig()
fmt = config['timefmt']
start_time = datetime.now().strftime(fmt)
try:
if onlinecheck.check_status(self.asset):
status = "online"
else:
status = "offline"
except socket.gaierror:
status = "an invalid asset tag."
msg =("{}: {} is {}. \n".format(start_time, self.asset, status))
wx.CallAfter(self.window.Logger, msg)
And in my wxPyhton Frame I have this function called from a Start button:
在我的wxPyhton框架中,我从“开始”按钮调用此函数:
def CheckAsset(self, asset):
self.count += 1
thread = PingAssets(self.count, asset, self)
self.threads.append(thread)
thread.start()
4 个解决方案
#1
21
This has been asked before on Stack. See the following links:
这已经在Stack上被问过了。请参阅以下链接:
- Is there any way to kill a Thread in Python?
- 有没有办法在Python中杀死一个线程?
- Stopping a thread after a certain amount of time
- 在一段时间后停止线程
Basically you just need to set up the thread with a stop function that sets a sentinel value that the thread will check. In your case, you'll have the something in your loop check the sentinel value to see if it's changed and if it has, the loop can break and the thread can die.
基本上你只需要设置一个带有stop函数的线程,该函数设置线程将检查的sentinel值。在你的情况下,你将循环中的东西检查sentinel值以查看它是否已更改,如果有,则循环可能会中断并且线程可能会死亡。
#2
50
Threaded stoppable function
Instead of subclassing threading.Thread
, one can modify the function to allow stopping by a flag.
可以修改函数以允许通过标志停止,而不是子类化threading.Thread。
We need an object, accessible to running function, to which we set the flag to stop running.
我们需要一个可以运行的函数的对象,我们将该标志设置为停止运行。
We can use threading.currentThread()
object.
我们可以使用threading.currentThread()对象。
import threading
import time
def doit(arg):
t = threading.currentThread()
while getattr(t, "do_run", True):
print ("working on %s" % arg)
time.sleep(1)
print("Stopping as you wish.")
def main():
t = threading.Thread(target=doit, args=("task",))
t.start()
time.sleep(5)
t.do_run = False
t.join()
if __name__ == "__main__":
main()
The trick is, that the running thread can have attached additional properties. The solution builds on assumptions:
诀窍是,正在运行的线程可以附加其他属性。该解决方案建立在假设的基础上
- the thread has a property "do_run" with default value
True
- 该线程有一个属性“do_run”,默认值为True
- driving parent process can assign to started thread the property "do_run" to
False
. - 驱动父进程可以将已启动线程的属性“do_run”分配给False。
Running the code, we get following output:
运行代码,我们得到以下输出:
$ python stopthread.py
working on task
working on task
working on task
working on task
working on task
Stopping as you wish.
Pill to kill - using Event
Other alternative is to use threading.Event
as function argument. It is by default False
, but external process can "set it" (to True
) and function can learn about it using wait(timeout)
function.
另一种方法是使用threading.Event作为函数参数。它默认为False,但外部进程可以“设置”(为True),函数可以使用wait(超时)函数了解它。
We can wait
with zero timeout, but we can also use it as the sleeping timer (used below).
我们可以等待零超时,但我们也可以将它用作休眠计时器(在下面使用)。
def doit(stop_event, arg):
while not stop_event.wait(1):
print ("working on %s" % arg)
print("Stopping as you wish.")
def main():
pill2kill = threading.Event()
t = threading.Thread(target=doit, args=(pill2kill, "task"))
t.start()
time.sleep(5)
pill2kill.set()
t.join()
Edit: I tried this in Python 3.6. stop_event.wait()
blocks the event (and so the while loop) until release. It does not return a boolean value. Using stop_event.is_set()
works instead.
编辑:我在Python 3.6中尝试过这个。 stop_event.wait()阻止事件(以及while循环)直到释放。它不返回布尔值。使用stop_event.is_set()可以正常工作。
Stopping multiple threads with one pill
Advantage of pill to kill is better seen, if we have to stop multiple threads at once, as one pill will work for all.
如果我们必须立即停止多个线程,可以更好地看到杀死药丸的优势,因为一个药丸将适用于所有人。
The doit
will not change at all, only the main
handles the threads a bit differently.
doit根本不会改变,只有主要处理线程有点不同。
def main():
pill2kill = threading.Event()
tasks = ["task ONE", "task TWO", "task THREE"]
def thread_gen(pill2kill, tasks):
for task in tasks:
t = threading.Thread(target=doit, args=(pill2kill, task))
yield t
threads = list(thread_gen(pill2kill, tasks))
for thread in threads:
thread.start()
time.sleep(5)
pill2kill.set()
for thread in threads:
thread.join()
#3
9
I read the other questions on Stack but I was still a little confused on communicating across classes. Here is how I approached it:
我在Stack上阅读了其他问题,但我仍然对跨类通信感到困惑。这是我接近它的方式:
I use a list to hold all my threads in the __init__
method of my wxFrame class: self.threads = []
我使用一个列表来保存我的wxFrame类的__init__方法中的所有线程:self.threads = []
As recommended in How to stop a looping thread in Python? I use a signal in my thread class which is set to True
when initializing the threading class.
如何在Python中停止循环线程中的建议?我在我的线程类中使用一个信号,在初始化线程类时将其设置为True。
class PingAssets(threading.Thread):
def __init__(self, threadNum, asset, window):
threading.Thread.__init__(self)
self.threadNum = threadNum
self.window = window
self.asset = asset
self.signal = True
def run(self):
while self.signal:
do_stuff()
sleep()
and I can stop these threads by iterating over my threads:
我可以通过遍历我的线程来阻止这些线程:
def OnStop(self, e):
for t in self.threads:
t.signal = False
#4
0
I had a different approach. I've sub-classed a Thread class and in the constructor I've created an Event object. Then I've written custom join() method, which first sets this event and then calls a parent's version of itself.
我有一个不同的方法。我已经对一个Thread类进行了细分,在构造函数中我创建了一个Event对象。然后我编写了自定义join()方法,该方法首先设置此事件,然后调用父本身的版本。
Here is my class, I'm using for serial port communication in wxPython app:
这是我的课,我在wxPython app中用于串口通信:
import wx, threading, serial, Events, Queue
class PumpThread(threading.Thread):
def __init__ (self, port, queue, parent):
super(PumpThread, self).__init__()
self.port = port
self.queue = queue
self.parent = parent
self.serial = serial.Serial()
self.serial.port = self.port
self.serial.timeout = 0.5
self.serial.baudrate = 9600
self.serial.parity = 'N'
self.stopRequest = threading.Event()
def run (self):
try:
self.serial.open()
except Exception, ex:
print ("[ERROR]\tUnable to open port {}".format(self.port))
print ("[ERROR]\t{}\n\n{}".format(ex.message, ex.traceback))
self.stopRequest.set()
else:
print ("[INFO]\tListening port {}".format(self.port))
self.serial.write("FLOW?\r")
while not self.stopRequest.isSet():
msg = ''
if not self.queue.empty():
try:
command = self.queue.get()
self.serial.write(command)
except Queue.Empty:
continue
while self.serial.inWaiting():
char = self.serial.read(1)
if '\r' in char and len(msg) > 1:
char = ''
#~ print('[DATA]\t{}'.format(msg))
event = Events.PumpDataEvent(Events.SERIALRX, wx.ID_ANY, msg)
wx.PostEvent(self.parent, event)
msg = ''
break
msg += char
self.serial.close()
def join (self, timeout=None):
self.stopRequest.set()
super(PumpThread, self).join(timeout)
def SetPort (self, serial):
self.serial = serial
def Write (self, msg):
if self.serial.is_open:
self.queue.put(msg)
else:
print("[ERROR]\tPort {} is not open!".format(self.port))
def Stop(self):
if self.isAlive():
self.join()
The Queue is used for sending messages to the port and main loop takes responses back. I've used no serial.readline() method, because of different end-line char, and I have found the usage of io classes to be too much fuss.
队列用于向端口发送消息,主循环接收响应。我没有使用serial.readline()方法,因为不同的终端字符串,我发现io类的用法太过分了。
#1
21
This has been asked before on Stack. See the following links:
这已经在Stack上被问过了。请参阅以下链接:
- Is there any way to kill a Thread in Python?
- 有没有办法在Python中杀死一个线程?
- Stopping a thread after a certain amount of time
- 在一段时间后停止线程
Basically you just need to set up the thread with a stop function that sets a sentinel value that the thread will check. In your case, you'll have the something in your loop check the sentinel value to see if it's changed and if it has, the loop can break and the thread can die.
基本上你只需要设置一个带有stop函数的线程,该函数设置线程将检查的sentinel值。在你的情况下,你将循环中的东西检查sentinel值以查看它是否已更改,如果有,则循环可能会中断并且线程可能会死亡。
#2
50
Threaded stoppable function
Instead of subclassing threading.Thread
, one can modify the function to allow stopping by a flag.
可以修改函数以允许通过标志停止,而不是子类化threading.Thread。
We need an object, accessible to running function, to which we set the flag to stop running.
我们需要一个可以运行的函数的对象,我们将该标志设置为停止运行。
We can use threading.currentThread()
object.
我们可以使用threading.currentThread()对象。
import threading
import time
def doit(arg):
t = threading.currentThread()
while getattr(t, "do_run", True):
print ("working on %s" % arg)
time.sleep(1)
print("Stopping as you wish.")
def main():
t = threading.Thread(target=doit, args=("task",))
t.start()
time.sleep(5)
t.do_run = False
t.join()
if __name__ == "__main__":
main()
The trick is, that the running thread can have attached additional properties. The solution builds on assumptions:
诀窍是,正在运行的线程可以附加其他属性。该解决方案建立在假设的基础上
- the thread has a property "do_run" with default value
True
- 该线程有一个属性“do_run”,默认值为True
- driving parent process can assign to started thread the property "do_run" to
False
. - 驱动父进程可以将已启动线程的属性“do_run”分配给False。
Running the code, we get following output:
运行代码,我们得到以下输出:
$ python stopthread.py
working on task
working on task
working on task
working on task
working on task
Stopping as you wish.
Pill to kill - using Event
Other alternative is to use threading.Event
as function argument. It is by default False
, but external process can "set it" (to True
) and function can learn about it using wait(timeout)
function.
另一种方法是使用threading.Event作为函数参数。它默认为False,但外部进程可以“设置”(为True),函数可以使用wait(超时)函数了解它。
We can wait
with zero timeout, but we can also use it as the sleeping timer (used below).
我们可以等待零超时,但我们也可以将它用作休眠计时器(在下面使用)。
def doit(stop_event, arg):
while not stop_event.wait(1):
print ("working on %s" % arg)
print("Stopping as you wish.")
def main():
pill2kill = threading.Event()
t = threading.Thread(target=doit, args=(pill2kill, "task"))
t.start()
time.sleep(5)
pill2kill.set()
t.join()
Edit: I tried this in Python 3.6. stop_event.wait()
blocks the event (and so the while loop) until release. It does not return a boolean value. Using stop_event.is_set()
works instead.
编辑:我在Python 3.6中尝试过这个。 stop_event.wait()阻止事件(以及while循环)直到释放。它不返回布尔值。使用stop_event.is_set()可以正常工作。
Stopping multiple threads with one pill
Advantage of pill to kill is better seen, if we have to stop multiple threads at once, as one pill will work for all.
如果我们必须立即停止多个线程,可以更好地看到杀死药丸的优势,因为一个药丸将适用于所有人。
The doit
will not change at all, only the main
handles the threads a bit differently.
doit根本不会改变,只有主要处理线程有点不同。
def main():
pill2kill = threading.Event()
tasks = ["task ONE", "task TWO", "task THREE"]
def thread_gen(pill2kill, tasks):
for task in tasks:
t = threading.Thread(target=doit, args=(pill2kill, task))
yield t
threads = list(thread_gen(pill2kill, tasks))
for thread in threads:
thread.start()
time.sleep(5)
pill2kill.set()
for thread in threads:
thread.join()
#3
9
I read the other questions on Stack but I was still a little confused on communicating across classes. Here is how I approached it:
我在Stack上阅读了其他问题,但我仍然对跨类通信感到困惑。这是我接近它的方式:
I use a list to hold all my threads in the __init__
method of my wxFrame class: self.threads = []
我使用一个列表来保存我的wxFrame类的__init__方法中的所有线程:self.threads = []
As recommended in How to stop a looping thread in Python? I use a signal in my thread class which is set to True
when initializing the threading class.
如何在Python中停止循环线程中的建议?我在我的线程类中使用一个信号,在初始化线程类时将其设置为True。
class PingAssets(threading.Thread):
def __init__(self, threadNum, asset, window):
threading.Thread.__init__(self)
self.threadNum = threadNum
self.window = window
self.asset = asset
self.signal = True
def run(self):
while self.signal:
do_stuff()
sleep()
and I can stop these threads by iterating over my threads:
我可以通过遍历我的线程来阻止这些线程:
def OnStop(self, e):
for t in self.threads:
t.signal = False
#4
0
I had a different approach. I've sub-classed a Thread class and in the constructor I've created an Event object. Then I've written custom join() method, which first sets this event and then calls a parent's version of itself.
我有一个不同的方法。我已经对一个Thread类进行了细分,在构造函数中我创建了一个Event对象。然后我编写了自定义join()方法,该方法首先设置此事件,然后调用父本身的版本。
Here is my class, I'm using for serial port communication in wxPython app:
这是我的课,我在wxPython app中用于串口通信:
import wx, threading, serial, Events, Queue
class PumpThread(threading.Thread):
def __init__ (self, port, queue, parent):
super(PumpThread, self).__init__()
self.port = port
self.queue = queue
self.parent = parent
self.serial = serial.Serial()
self.serial.port = self.port
self.serial.timeout = 0.5
self.serial.baudrate = 9600
self.serial.parity = 'N'
self.stopRequest = threading.Event()
def run (self):
try:
self.serial.open()
except Exception, ex:
print ("[ERROR]\tUnable to open port {}".format(self.port))
print ("[ERROR]\t{}\n\n{}".format(ex.message, ex.traceback))
self.stopRequest.set()
else:
print ("[INFO]\tListening port {}".format(self.port))
self.serial.write("FLOW?\r")
while not self.stopRequest.isSet():
msg = ''
if not self.queue.empty():
try:
command = self.queue.get()
self.serial.write(command)
except Queue.Empty:
continue
while self.serial.inWaiting():
char = self.serial.read(1)
if '\r' in char and len(msg) > 1:
char = ''
#~ print('[DATA]\t{}'.format(msg))
event = Events.PumpDataEvent(Events.SERIALRX, wx.ID_ANY, msg)
wx.PostEvent(self.parent, event)
msg = ''
break
msg += char
self.serial.close()
def join (self, timeout=None):
self.stopRequest.set()
super(PumpThread, self).join(timeout)
def SetPort (self, serial):
self.serial = serial
def Write (self, msg):
if self.serial.is_open:
self.queue.put(msg)
else:
print("[ERROR]\tPort {} is not open!".format(self.port))
def Stop(self):
if self.isAlive():
self.join()
The Queue is used for sending messages to the port and main loop takes responses back. I've used no serial.readline() method, because of different end-line char, and I have found the usage of io classes to be too much fuss.
队列用于向端口发送消息,主循环接收响应。我没有使用serial.readline()方法,因为不同的终端字符串,我发现io类的用法太过分了。