如何在 Python 中停止循环线程? [英] How to stop a looping thread in Python?

查看:48
本文介绍了如何在 Python 中停止循环线程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

告诉循环线程停止循环的正确方法是什么?

What's the proper way to tell a looping thread to stop looping?

我有一个相当简单的程序,它在一个单独的 threading.Thread 类中 ping 一个指定的主机.在这个类中它会休眠 60 秒,然后再次运行直到应用程序退出.

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.

我想在我的 wx.Frame 中实现一个停止"按钮来要求循环线程停止.它不需要立即结束线程,它可以在唤醒后停止循环.

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.

这是我的 threading 类(注意:我还没有实现循环,但它可能属于 PingAssets 中的 run 方法)

Here is my threading class (note: I haven't implemented looping yet, but it would likely fall under the run method in PingAssets)

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 {}.   
".format(start_time, self.asset, status))
        wx.CallAfter(self.window.Logger, msg)

在我的 wxPyhton 框架中,我从开始"按钮调用了这个函数:

And in my wxPyhton Frame I have this function called from a Start button:

def CheckAsset(self, asset):
        self.count += 1
        thread = PingAssets(self.count, asset, self)
        self.threads.append(thread)
        thread.start()

推荐答案

线程可停止函数

代替子类化 threading.Thread,可以修改函数以允许停在一个标志.

Threaded stoppable function

Instead of subclassing threading.Thread, one can modify the function to allow stopping by a flag.

我们需要一个可以访问运行函数的对象,我们将标志设置为停止运行.

We need an object, accessible to running function, to which we set the flag to stop running.

我们可以使用 threading.currentThread() 对象.

We can use threading.currentThread() object.

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
    

if __name__ == "__main__":
    main()

诀窍是,正在运行的线程可以附加其他属性.解决方案构建假设:

The trick is, that the running thread can have attached additional properties. The solution builds on assumptions:

  • 线程有一个属性do_run";默认值 True
  • 驱动父进程可以将属性do_run"分配给启动线程..

运行代码,我们得到如下输出:

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.

药丸杀死 - 使用事件

其他替代方法是使用 threading.Event 作为函数参数.它是由默认False,但外部进程可以设置它";(to True) 和函数可以使用 wait(timeout) 函数了解它.

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.

我们可以在零超时的情况下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()

我在 Python 3.6 中尝试过这个.stop_event.wait() 阻塞事件(以及 while 循环)直到释放.它不返回布尔值.使用 stop_event.is_set() 代替.

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.

如果我们必须停止多个线程,则更好地看到药丸杀死的优势一次,因为一粒药对所有人都有效.

Advantage of pill to kill is better seen, if we have to stop multiple threads at once, as one pill will work for all.

doit 根本不会改变,只有 main 处理线程有点不同.

The doit will not change at all, only the main handles the threads a bit differently.

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

这篇关于如何在 Python 中停止循环线程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆