How to Stop a Looping Thread in Python

How to stop a looping thread in Python?

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

How do I stop a thread in python which itself is being called inside a loop?

So as an update, I have managed to resolve this issue. The problem with the other answer stated by me (shown below) is that just .cancel() by itself only seemed to work for one timer thread. But as can be seen in the problem, childFunction() itself calls childFunction() and can also be called by the parentFunction, meaning that there may be multiple timer threads.

What worked for my specific case was naming my threads as below:

t1 = threading.Timer(10, childFunction, args=(var1,var2,number))
t1.name = t1.name + "_timer" + str(number)
t1.start()

Thereafter, I could cancel all timer threads that were created from this process by:

for timerthread in threading.enumerate():
if timerthread.name.endswith('timer' + str(number)):
timerthread.cancel()

Below is the ORIGINAL METHOD I USED WHICH CAUSED MANY ISSUES:

I'm not certain if this is a bad practice (in fact I feel it may be based on the answers linked in the question saying that we should never 'kill a thread'). I'm sure there are reasons why this is not good and I'd appreciate anyone telling me why. However, the solution that ultimately worked for me was to use .cancel().

So first change would be to assign your thread Timer to a variable instead of calling it directly. So instead of threading.Timer(10, childFunction, args=(var1,var2)).start(), it should be

t = threading.Timer(10, childFunction, args=(var1,var2))
t.start()

Following that, instead of end_process_and_exit_here(), you should use t.cancel(). This seems to work and stops all threads mid-process. However, the bad thing is that it doesn't seem to carry on with other parts of the program.

How to end a while loop in another Thread?

Note your code isn't passing the arguments properly when creating the Thread, and never set the condition boolean variable to True.

Here's how to fix those things and be able to stop the while loop in the other thread. Instead of a simple boolean variable, it uses an Threading.Event object to control the loop and allow it to be set true from the main thread in a thread-safe way.

Code

from time import sleep
from threading import Event, Thread

condition = Event()

def do_sth():
print("truckin' ...")

def check_sth():
while not condition.is_set():
sleep(0.25)
do_sth() # Do something everytime the condition is not set.

print("Condition met, ending.")

Thread(target=check_sth, args=()).start()
sleep(2)
condition.set() # End while loop.

Output

truckin' ...
truckin' ...
truckin' ...
truckin' ...
truckin' ...
truckin' ...
truckin' ...
truckin' ...
Condition met, ending.

How to stop threads running infinite loops in python?

Your basic method does work, but you've still not posted enough code to show the flaw. I added a few lines of code to make it runnable and produced a result like:

$ python3 test.py
thread alive
main alive
thread alive
main alive
^CSignal caught
main alive
thread alive
main alive
main alive
main alive
^CSignal caught
^CSignal caught
main alive
^Z
[2]+ Stopped python3 test.py
$ kill %2

The problem demonstrated above involves the signal handler telling all the threads to exit, except the main thread, which still runs and still catches interrupts. The full source of this variant of the sample snippet is:

import threading, signal, time

def sighandler(signal,frame):
BaseThreadClass.stop_flag = True
print("Signal caught")

class BaseThreadClass(threading.Thread):
stop_flag = False
def __init__(self):
threading.Thread.__init__(self)
def run(self,*args):
while True:
if True:
time.sleep(1)
print("thread alive")
else:
#do computation and stuff
pass
if BaseThreadClass.stop_flag:
#do cleanup
break

signal.signal(signal.SIGINT, sighandler)

t = BaseThreadClass()
t.start()

while True:
time.sleep(1)
print("main alive")

The problem here is that the main thread never checks for the quit condition. But as you never posted what the main thread does, nor how the signal handler is activated, or information regarding whether threads may go a long time without checking the quit condition... I still don't know what went wrong in your program. The signal example shown in the library documentation raises an exception in order to divert the main thread.

Signals are a rather low level concept for this task, however. I took the liberty of writing a somewhat more naïve version of the main thread:

try:
t = BaseThreadClass()
t.start()
while True:
time.sleep(1)
print("main alive")
except KeyboardInterrupt:
BaseThreadClass.stop_flag = True
t.join()

This version catches the exception thrown by the default interrupt handler, signals the thread to stop, and waits for it to do so. It might even be appropriate to change the except clause to a finally, since we could want to clean the threads up on other errors too.

Ending a thread that contains an infinite loop

Reduced to a minimal example so it can run without hardware. Comments added for fixes. The main problem was stop was only checked after 256*iterations*wait_ms/1000.0 or 25.6 seconds, so stopping appeared unresponsive. A few other bugs as well. Global stop is sufficient to end the thread so the Event wasn't needed.

import time
import argparse
from threading import Thread
import sys

global stop

def rainbowCycle(strip, wait_ms=20, iterations=5):
"""Draw rainbow that uniformly distributes itself across all pixels."""
while not stop:
for j in range(256*iterations):
if stop: break # add stop check here for responsiveness.
print(f'rainbowCycle')
time.sleep(wait_ms/1000.0)
print('stopping...')

class RainbowLights(Thread):
def __init__(self, name="RainbowLights"):
print('__init__')
Thread.__init__(self, name=name)

def run(self):
print('run')
rainbowCycle(strip)
print('stopped')

if __name__ == '__main__':
strip = None
print('Press Ctrl-C to quit.')
rta = "none"
stop = False
rainbow_lights = RainbowLights() # renamed variable so it doesn't replace the class.

try:
time.sleep(2) # to stabilize sensor
while True:
trigger = 1 # pretend some motion
if trigger == 1:
print("Motion Detected...")
if rta == "running":
print("Lights Already On")
else:
rainbow_lights.start()
rta = "running"
time.sleep(5) #to avoid multiple detection
elif trigger == 0:
print("No Motion...")
time.sleep(0.1) #loop delay, should be less than detection delay

except KeyboardInterrupt:
print('KeyboardInterrupt')
print('stop')
stop = True
Press Ctrl-C to quit.
__init__
Motion Detected...
run
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
rainbowCycle
KeyboardInterrupt
stop
stopping...
stopped

Is there any way to kill a Thread?

It is generally a bad pattern to kill a thread abruptly, in Python, and in any language. Think of the following cases:

  • the thread is holding a critical resource that must be closed properly
  • the thread has created several other threads that must be killed as well.

The nice way of handling this, if you can afford it (if you are managing your own threads), is to have an exit_request flag that each thread checks on a regular interval to see if it is time for it to exit.

For example:

import threading

class StoppableThread(threading.Thread):
"""Thread class with a stop() method. The thread itself has to check
regularly for the stopped() condition."""

def __init__(self, *args, **kwargs):
super(StoppableThread, self).__init__(*args, **kwargs)
self._stop_event = threading.Event()

def stop(self):
self._stop_event.set()

def stopped(self):
return self._stop_event.is_set()

In this code, you should call stop() on the thread when you want it to exit, and wait for the thread to exit properly using join(). The thread should check the stop flag at regular intervals.

There are cases, however, when you really need to kill a thread. An example is when you are wrapping an external library that is busy for long calls, and you want to interrupt it.

The following code allows (with some restrictions) to raise an Exception in a Python thread:

def _async_raise(tid, exctype):
'''Raises an exception in the threads with id tid'''
if not inspect.isclass(exctype):
raise TypeError("Only types can be raised (not instances)")
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid),
ctypes.py_object(exctype))
if res == 0:
raise ValueError("invalid thread id")
elif res != 1:
# "if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None)
raise SystemError("PyThreadState_SetAsyncExc failed")

class ThreadWithExc(threading.Thread):
'''A thread class that supports raising an exception in the thread from
another thread.
'''
def _get_my_tid(self):
"""determines this (self's) thread id

CAREFUL: this function is executed in the context of the caller
thread, to get the identity of the thread represented by this
instance.
"""
if not self.isAlive():
raise threading.ThreadError("the thread is not active")

# do we have it cached?
if hasattr(self, "_thread_id"):
return self._thread_id

# no, look for it in the _active dict
for tid, tobj in threading._active.items():
if tobj is self:
self._thread_id = tid
return tid

# TODO: in python 2.6, there's a simpler way to do: self.ident

raise AssertionError("could not determine the thread's id")

def raiseExc(self, exctype):
"""Raises the given exception type in the context of this thread.

If the thread is busy in a system call (time.sleep(),
socket.accept(), ...), the exception is simply ignored.

If you are sure that your exception should terminate the thread,
one way to ensure that it works is:

t = ThreadWithExc( ... )
...
t.raiseExc( SomeException )
while t.isAlive():
time.sleep( 0.1 )
t.raiseExc( SomeException )

If the exception is to be caught by the thread, you need a way to
check that your thread has caught it.

CAREFUL: this function is executed in the context of the
caller thread, to raise an exception in the context of the
thread represented by this instance.
"""
_async_raise( self._get_my_tid(), exctype )

(Based on Killable Threads by Tomer Filiba. The quote about the return value of PyThreadState_SetAsyncExc appears to be from an old version of Python.)

As noted in the documentation, this is not a magic bullet because if the thread is busy outside the Python interpreter, it will not catch the interruption.

A good usage pattern of this code is to have the thread catch a specific exception and perform the cleanup. That way, you can interrupt a task and still have proper cleanup.

Stopping a python thread running an Infinite Loop

I think you missed the 'The thread itself has to check regularly for the stopped() condition' bit of that documentation.

Your thread needs to run like this:

while not self.stopped():
# do stuff

rather than while true. Note that it is still only going to exit at the 'start' of a loop, when it checks the condition. If whatever is in that loop is long-running, that may cause unexpected delays.

How to stop / break out of threading infinite while-loop?

Maybe this

flag = False

def start_button():
flag = True
t1 = threading.Thread(target=thread1)
t1.start()

def thread1():
while flag:
start()

def stop_button():
flag = false


Related Topics



Leave a reply



Submit