Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stop a looping thread in Python?

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.

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.

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 {}.   \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:

def CheckAsset(self, asset):         self.count += 1         thread = PingAssets(self.count, asset, self)         self.threads.append(thread)         thread.start() 
like image 968
pedram Avatar asked Aug 02 '13 13:08

pedram


People also ask

How do I stop a thread loop?

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.

How do I stop a Python thread from running?

Using a hidden function _stop() : In order to kill a thread, we use hidden function _stop() this function is not documented but might disappear in the next version of python.

How do I stop a thread from within?

It's easy to use, just implement a stop() inside your thread class.


2 Answers

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.

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:

  • the thread has a property "do_run" with default value True
  • driving parent process can assign to started thread the property "do_run" to 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.

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.

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.

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() 
like image 151
Jan Vlcinsky Avatar answered Oct 14 '22 21:10

Jan Vlcinsky


This has been asked before on Stack. See the following links:

  • Is there any way to kill a Thread in 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.

like image 37
Mike Driscoll Avatar answered Oct 14 '22 23:10

Mike Driscoll