Stopping a python thread running an Infinite Loop - python

I am new to python programming. I am trying to make a GUI with stoppable threads.
I borrowed some code from
https://stackoverflow.com/a/325528
class MyThread(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(MyThread, self).__init__(*args, **kwargs)
self._stop = threading.Event()
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
I have function which creates a thread for another function in another class that runs an infinite loop.
class MyClass :
def clicked_practice(self):
self.practicethread = MyThread(target=self.infinite_loop_method)
self.practicethread.start()
def infinite_loop_method()
while True :
// Do something
#This doesn't seem to work and I am still stuck in the loop
def infinite_stop(self)
if self.practicethread.isAlive():
self.practicethread.stop()
I want to create a method to stop this thread .
What's happening here?

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.

import threading
import time
class MultiThreading:
def __init__(self):
self.thread = None
self.started = True
def threaded_program(self):
while self.started:
print("running")
# time.sleep(10)
def run(self):
self.thread = threading.Thread(target=self.threaded_program, args=())
self.thread.start()
def stop(self):
self.started = False
self.thread.join()

Related

Killing a python thread

I would like to kill somehow a running thread from my GUI application via setting an event, but I can't use a for loop in my thread so I need some other solution to check the event
I have the following situation.
In a tkinter gui when I click a button I start a thread and set a global variable.
self.thread = StoppableThread(caller=self)
self.thread.start()
is_running = 1
When I next click the button I check the global variable state and if it is already set I send a stop request:
if is_running:
is_running = 0
self.thread.stop()
This is my thread class:
import threading
from time import sleep
class StoppableThread(threading.Thread):
def __init__(self, caller=None):
super(StoppableThread, self).__init__()
self._stop_event = threading.Event()
self.caller = caller
def stop(self):
self._stop_event.set()
def stopped(self):
return self._stop_event.is_set()
def run(self) -> None:
while True:
# check for stop
if self.stopped():
break
for i in range(10):
sleep(1)
print('Test')
print('Worker done')
break
Everything works if I change the while to a for loop, but because in this point in my business logic I doesn't have anything to loop for I need to check somehow different the state of the self.stopped(). Is there any way to check it in the while loop?
Or how can I achive this? I tried to use process instead of thread but it wasnt worked because of an error 'process can't pickle tkinter'.
Thank you for any help
This loop will run forever until you set the flag:
def run(self):
while not self.stopped():
sleep(1)
print('Test')
You don't actually need an event. A simple Boolean will do.
FOLLOWUP
Here's an example based on your code that shows how this works:
import threading
from time import sleep
class StoppableThread(threading.Thread):
def __init__(self, caller=None):
super(StoppableThread, self).__init__()
self._stop_event = False
self.caller = caller
def stop(self):
self._stop_event = True
def stopped(self):
return self._stop_event
def run(self) -> None:
while not self.stopped():
sleep(1)
print('Test')
print("exited")
thread = StoppableThread(caller=None)
thread.start()
sleep(5)
thread.stop()
sleep(1)
print("program ending")

How to stop a thread gracefully with Cntrl+C in Python

I have a class Controller with a method job which I'd like to run at regular intervals using the schedule module. Further, I'd like to have several 'variations' of this job running on separate threads such that they are all can be gracefully interrupted using Cntrl+C. (I do not want to make the threads daemon threads and shut them down abruptly).
Here is what I have so far:
import schedule
import threading
import time
import signal
import sys
class Controller(object):
def __init__(self, name="controller", interval=1):
self.name = name
self.interval = interval
def job(self):
print("My name is {}.".format(self.name))
class ThreadController(threading.Thread):
def __init__(self, *args, **kwargs):
super(ThreadController, self).__init__()
self.controller = Controller(*args, **kwargs)
self._stop = threading.Event()
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
def run(self):
schedule.every(self.controller.interval).seconds.do(self.controller.job)
while not self.stopped():
schedule.run_pending()
if __name__ == "__main__":
controller1 = ThreadController(name="foo")
controller2 = ThreadController(name="bar")
try:
controller1.start()
controller2.start()
time.sleep(1000) # This ensures that the execution remains within the 'try' block (for a significant amount of time)
except KeyboardInterrupt:
controller1.stop()
controller2.stop()
The program works, in that for the first 1000 seconds it will alternately print My name is foo. and My name is bar. until Cntrl+C is pressed.
To make the code remain within the try block, however, I am for the time being using time.sleep which is not an elegant solution. What I actually want is to 'wait' until Cntrl+C is pressed. Is there an elegant way to implement this?
(Another thing I tried is the following, after Capture keyboardinterrupt in Python without try-except:
if __name__ == "__main__":
controller1 = ThreadController(name="foo")
controller2 = ThreadController(name="bar")
def signal_handler(signal, frame):
print("Stopping threads and exiting...")
controller1.stop()
controller2.stop()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
controller1.start()
controller2.start()
but this seems not to work, as the program keeps printing after Cntrl+C is pressed).
Ctrl+C terminates you main thread and controller1 and controller2 are still running.
You may demonize them
controller1.daemon = True
controller2.daemon = True
before starting. But when you main thread starts these two it will exit and shut down them as well.
So in order to keep it busy run a infinite loop in it
while True:
sleep(0.1)
For the time being I'm going with an infinite loop like the one suggested by Alexey Smirnov. The implementation is slightly different and uses Python's signal:
import schedule
import threading
import time
import signal
import sys
class Controller(object):
def __init__(self, name="controller", interval=1):
self.name = name
self.interval = interval
def job(self):
print("My name is {}.".format(self.name))
class ThreadController(threading.Thread):
def __init__(self, *args, **kwargs):
super(ThreadController, self).__init__()
self.controller = Controller(*args, **kwargs)
self._stop = threading.Event()
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
def run(self):
schedule.every(self.controller.interval).seconds.do(self.controller.job)
while not self.stopped():
schedule.run_pending()
def signal_handler(signum, frame):
controller_threads = [thread for thread in threading.enumerate() if isinstance(thread, ThreadController)]
for controller_thread in controller_threads:
print("Stopping {}.".format(controller_thread))
controller_thread.stop()
sys.exit(1)
if __name__ == "__main__":
controller1 = ThreadController(name="foo")
controller2 = ThreadController(name="bar")
signal.signal(signal.SIGINT, signal_handler)
controller1.start()
controller2.start()
while True: time.sleep(0.1) # Keep the main thread alive until interrupted
The advantage of not using daemon threads is that they are not abruptly, but gracefully shut down.

Stopping a thread in python

Is there a way in python to stop a thread? I have a gui with a method which plays 10 second audio files and sends information to GUI window continuously
I am Multithreading because I dont want the GUI to freeze while my files play. I can stop the thread with my current code but takes a while
My code looks something like this :
class MyThread(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(MyThread, self).__init__(*args, **kwargs)
self._stop = threading.Event()
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
class Myplayer:
// GUI CODE
def play_button(self, widget):
self.mythread = MyThread(target=self.practice)
self.mythread.start()
def stop_button(self, widget):
if self.mythead.IsAlive:
self.self.stop()
def mplayer(self):
while not self.mythread.stopped:
gobject.idle_add(self.insert_text, "\nPlaying a new file")
subprocess.call(["timidity", "myfile.mid"])
Assuming you want to interrupt your midi file while it's playing if your thread is stopped, you can stop the thread more quickly by using Popen instead of call, and then waiting in a loop for either the process to finish, or for the stop request to come in:
class MyThread(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(MyThread, self).__init__(*args, **kwargs)
self._stop = threading.Event()
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
def run(self):
while not self.stopped:
gobject.idle_add(self.insert_text, "\nPlaying a new file")
p = subprocess.Popen(["timidity", "myfile.mid"])
while p.poll() is None: # This will loop until the process has exited.
if self.stopped:
# Someone set the stop flag. Kill the process and exit
p.terminate()
p.join()
return
time.sleep(.1) # Sleep briefly so we don't hog CPU cycles
Here's an example. We start a single thread which does all the work. After two seconds, we tell it to die by setting the shared Event flag.
The worker thread generally runs in a loop, doing a little processing, then checking the flag. If it's set, then it exits, otherwise the thread does some more work.
source
import time
from threading import *
class WorkerThread(Thread):
def __init__(self, die_flag, *args, **kw):
super(WorkerThread,self).__init__(*args, **kw)
self.die_flag = die_flag
def run(self):
for num in range(3):
if self.die_flag.is_set():
print "{}: bye".format(
current_thread().name
)
return
print "{}: num={}".format(
current_thread().name, num,
)
time.sleep(1)
flag = Event()
WorkerThread(name='whiskey', die_flag=flag).start()
time.sleep(2)
print '\nTELL WORKERS TO DIE'
flag.set()
print '\nWAITING FOR WORKERS'
for thread in enumerate():
if thread != current_thread():
print thread.name,
thread.join()
print
output
whiskey: num=0
whiskey: num=1
TELL WORKERS TO DIE
WAITING FOR WORKERS
whiskey whiskey: bye

How do I stop a thread with a blocking function call?

I'm uisng the psutil library in a thread, that posts my CPU usage statistics periodically. Here's a snippet:
class InformationThread(threading.Thread):
def __init__(self, *args, **kwargs):
threading.Thread.__init__(self)
def run(self):
while True:
cpu = psutil.cpu_percent(interval=600) #this is a blocking call
print cpu
I need to stop this thread but I can't seem to understand how. The method cpu_percent is a blocking function that will block for 600 seconds.
I've been digging around and all the examples I saw relied on a tight-loop that checked a flag to see whether the loop should be interrupted but in this case, I'm not sure how to kill the thread.
Set interval to 0.0 and implement a tighter inner loop in which you can check whether your thread should terminate. It shouldn't be difficult to time it so that the elapsed time between calls to cpu_percent() is roughly the same as 600.
You could add astop()method to yourInformationThreadclass that terminates itsrun()loop as shown the following. But note that it won't unblock acpu_percent()call already in progress.
class InformationThread(threading.Thread):
def __init__(self, *args, **kwargs):
threading.Thread.__init__(self)
self.daemon = True # OK for main to exit even if instance still running
self.running = False
self.status_lock = threading.Lock()
def run(self):
with self.status_lock:
self.running = True
while True:
with self.status_lock:
if not self.running:
break
cpu = psutil.cpu_percent(interval=600) # this is a blocking call
print cpu
def stop(self):
with self.status_lock:
self.running = False

How to pass and run a callback method in Python

I have a Manager (main thread), that creates other Threads to handle various operations.
I would like my Manager to be notified when a Thread it created ends (when run() method execution is finished).
I know I could do it by checking the status of all my threads with the Thread.isActive() method, but polling sucks, so I wanted to have notifications.
I was thinking of giving a callback method to the Threads, and call this function at the end of the run() method:
class Manager():
...
MyThread(self.on_thread_finished).start() # How do I pass the callback
def on_thread_finished(self, data):
pass
...
class MyThread(Thread):
...
def run(self):
....
self.callback(data) # How do I call the callback?
...
Thanks!
The thread can't call the manager unless it has a reference to the manager. The easiest way for that to happen is for the manager to give it to the thread at instantiation.
class Manager(object):
def new_thread(self):
return MyThread(parent=self)
def on_thread_finished(self, thread, data):
print thread, data
class MyThread(Thread):
def __init__(self, parent=None):
self.parent = parent
super(MyThread, self).__init__()
def run(self):
# ...
self.parent and self.parent.on_thread_finished(self, 42)
mgr = Manager()
thread = mgr.new_thread()
thread.start()
If you want to be able to assign an arbitrary function or method as a callback, rather than storing a reference to the manager object, this becomes a bit problematic because of method wrappers and such. It's hard to design the callback so it gets a reference to both the manager and the thread, which is what you will want. I worked on that for a while and did not come up with anything I'd consider useful or elegant.
Anything wrong with doing it this way?
from threading import Thread
class Manager():
def Test(self):
MyThread(self.on_thread_finished).start()
def on_thread_finished(self, data):
print "on_thread_finished:", data
class MyThread(Thread):
def __init__(self, callback):
Thread.__init__(self)
self.callback = callback
def run(self):
data = "hello"
self.callback(data)
m = Manager()
m.Test() # prints "on_thread_finished: hello"
If you want the main thread to wait for children threads to finish execution, you are probably better off using some kind of synchronization mechanism. If simply being notified when one or more threads has finished executing, a Condition is enough:
import threading
class MyThread(threading.Thread):
def __init__(self, condition):
threading.Thread.__init__(self)
self.condition = condition
def run(self):
print "%s done" % threading.current_thread()
with self.condition:
self.condition.notify()
condition = threading.Condition()
condition.acquire()
thread = MyThread(condition)
thread.start()
condition.wait()
However, using a Queue is probably better, as it makes handling multiple worker threads a bit easier.

Categories

Resources