It is general in programming in Python that when I have a function or something which when I call, it blocks my code to proceed. So I think the best way to unblock is using threads but If I need to stop a thread what should I do?
I tried this reference and I wrote this simple program:
import threading
from time import sleep
class my_thread(threading.Thread):
"""Thread class with a stop() method. The thread itself has to check
regularly for the stopped() condition."""
def __init__(self):
super(my_thread, self).__init__()
self._stop_event = threading.Event()
def stop(self):
print("stopping the thread")
self._stop_event.set()
def stopped(self):
value=self._stop_event.is_set()
print("value of stop event is",value)
return value
def run(self):
print("running the thread")
print("start function startt()")
self.startt()
def startt(self):
print("it is going to wait forever")
while True:
#wait forever
pass
print("This line never execute")
def main():
for i in range(0,3):
print("it is the main function")
sleep(1)
if __name__+'__main__':
thr=my_thread()
thr.start()
sleep(5)
thr.stop()
thr.stopped()
print("calling the main function")
main()
print("Exiting the whole program")
My problem is this program actually stop the thread but after printing the last line the program still runs. What I want is if I call the stop function thr.start() it starts the thread and run #wait forever line and if I call the stop function thr.stop() it stop the whole class and returns from #wait forever line to the main function.
EDIT--
As #a_guest answer I can fix it but my problem is general for example If I had this code instead of while True:
pythoncom.PumpMessages()
(or any other code)
what should I do?
Instead of
while True:
...
you should use
while not self.stopped():
...
Then it will break out of the while loop once you stop() the thread.
You can't "abort" a running thread so to stop it you'll have to have a mechanism in the thread itself that periodically checks if it should stop.
Regular threads keep running while the rest of your program (process) exits.
If you make your thread a 'daemon' thread however, it will get killed automatically when your program exits. To do that, set self.daemon=True in your thread's init method. More info https://docs.python.org/3/library/threading.html#threading.Thread.daemon
Related
This question pertains to Python 3.6.
I have a piece of code which has a thread from the threading library running, as well as the program's main thread. Both threads access an object that is not threadsafe, so I have them locking on a Condition object before using the object. The thread I spawned only needs to access/update that object once every 5 minutes, so there's a 5 minute sleep timer in the loop.
Currently, the main thread never gets a hold of the lock. When the second thread releases the Condition and starts waiting on the sleep() call, the main thread never wakes up/acquires the lock. It's as if the main thread has died.
class Loader:
def __init__(self, q):
...
self.queryLock = threading.Condition()
...
thread = Thread(target=self.threadfunc, daemon=True)
thread.start()
...
self.run()
def threadfunc(self):
...
while True:
self.queryLock.acquire()
[critical section #1]
self.queryLock.notify()
self.queryLock.release()
sleep(300)
def run(self):
...
while True:
...
self.queryLock.acquire()
[critical section #2]
self.queryLock.notify()
self.queryLock.release()
...
I believe you don't really need to use a Condition. It appears that a simple Lock would get the job done in your case. You don't actually verify that some condition is met and you don't use the Condition's special method wait().
That being said, regarding the code you provided, it seems that your main thread is too "quick", and re-acquires the lock before the other thread gets a chance.
Here is a slightly modified version of your code in which the main thread is waiting a bit, giving the other thread a chance, and it successfully acquires the lock and continue.
class Loader:
def __init__(self):
self.queryLock = threading.Condition()
thread = Thread(target=self.threadfunc, daemon=True)
thread.start()
self.run()
def threadfunc(self):
while True:
self.queryLock.acquire()
print("critical section 1")
time.sleep(1)
self.queryLock.notify()
self.queryLock.release()
time.sleep(5)
def run(self):
while True:
self.queryLock.acquire()
print("critical section 2")
time.sleep(2)
self.queryLock.notify()
self.queryLock.release()
print("main is waiting a bit")
time.sleep(1)
Loader()
Race conditions are fun :)
When I run a While True loop in thread and use time.sleep() function the loop stops looping.
I am using this code:
import threading
from time import sleep
class drive_worker(threading.Thread):
def __init__(self):
super(drive_worker, self).__init__()
self.daemon = True
self.start()
def run(self):
while True:
print('loop')
#some code
time.sleep(0.5)
To start the thread I am using this code:
thread = drive_worker()
The loop stops because you flagged the thread as daemon.
The program terminates when there are only daemon threads left running.
self.daemon = True # remove this statement and the code should work as expected
Or make the main thread wait for the the daemon thread to finish
dthread = drive_worker()
# no call to start method since your constructor does that
dthread.join() #now the main thread waits for the new thread to finish
You imported sleep as
from time import sleep
so you have to call sleep in run() as sleep(0.5) or you have to change import as
import time
which I do not recommend.
Suppose I would like to run a function, called run_forever(), in a thread, but still have it 'stoppable' by pressing Ctrl+C. I've seen ways of doing this using a StoppableThread subclass of threading.Thread, but these seem to involve 'copying' the target function into that subclass. I would like to instead keep the function 'where it is'.
Consider the following example:
import time
import threading
def run_forever(): # An externally defined function which runs indefinitely
while True:
print("Hello, world!")
time.sleep(1)
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 = threading.Event()
def stop(self):
self._stop.set()
def stopped(self):
return self._stop.isSet()
def run(self):
while not self.stopped():
run_forever() # This doesn't work
# print("Hello, world!") # This does
self._stop.wait(1)
thread = StoppableThread()
thread.start()
time.sleep(5)
thread.stop()
The target function run_forever is itself a while-loop which never exits. However, to get the desired behavior the wait() command has to be inside that while-loop, as I understand it.
Is there any way of achieving the desired behavior without modifying the run_forever() function?
I doubt it's possible.
BTW, have you tried the second solution with
ThreadWithExc from the post you linked earlier?
It works if the loop is busy pure Python(eg no sleep), otherwise I'd switch to multiprocessing and kill subprocess. Here is the code that hopefully exits gracefully(*nix only):
from multiprocessing import Process
from signal import signal, SIGTERM
import time
def on_sigterm(*va):
raise SystemExit
def fun():
signal(SIGTERM, on_sigterm)
try:
for i in xrange(5):
print 'tick', i
time.sleep(1)
finally:
print 'graceful cleanup'
if __name__=='__main__':
proc = Process(target=fun)
proc.start()
time.sleep(2.5)
proc.terminate()
proc.join()
I am writing a python script that needs to run a thread which listens to a network socket.
I'm having trouble with killing it using Ctrl+c using the code below:
#!/usr/bin/python
import signal, sys, threading
THREADS = []
def handler(signal, frame):
global THREADS
print "Ctrl-C.... Exiting"
for t in THREADS:
t.alive = False
sys.exit(0)
class thread(threading.Thread):
def __init__(self):
self.alive = True
threading.Thread.__init__(self)
def run(self):
while self.alive:
# do something
pass
def main():
global THREADS
t = thread()
t.start()
THREADS.append(t)
if __name__ == '__main__':
signal.signal(signal.SIGINT, handler)
main()
Appreciate any advise on how to catch Ctrl+c and terminate the script.
The issue is that after the execution falls off the main thread (after main() returned), the threading module will pause, waiting for the other threads to finish, using locks; and locks cannot be interrupted with signals. This is the case in Python 2.x at least.
One easy fix is to avoid falling off the main thread, by adding an infinite loop that calls some function that sleeps until some action is available, like select.select(). If you don't need the main thread to do anything at all, use signal.pause(). Example:
if __name__ == '__main__':
signal.signal(signal.SIGINT, handler)
main()
while True: # added
signal.pause() # added
It's because signals can only be caught by main thread. And here main thread ended his life long time ago (application is waiting for your thread to finish). Try adding
while True:
sleep(1)
to the end of your main() (and of course from time import sleep at the very top).
or as Kevin said:
for t in THREADS:
t.join(1) # join with timeout. Without timeout signal cannot be caught.
I am testing Python threading with the following script:
import threading
class FirstThread (threading.Thread):
def run (self):
while True:
print 'first'
class SecondThread (threading.Thread):
def run (self):
while True:
print 'second'
FirstThread().start()
SecondThread().start()
This is running in Python 2.7 on Kubuntu 11.10. Ctrl+C will not kill it. I also tried adding a handler for system signals, but that did not help:
import signal
import sys
def signal_handler(signal, frame):
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
To kill the process I am killing it by PID after sending the program to the background with Ctrl+Z, which isn't being ignored. Why is Ctrl+C being ignored so persistently? How can I resolve this?
Ctrl+C terminates the main thread, but because your threads aren't in daemon mode, they keep running, and that keeps the process alive. We can make them daemons:
f = FirstThread()
f.daemon = True
f.start()
s = SecondThread()
s.daemon = True
s.start()
But then there's another problem - once the main thread has started your threads, there's nothing else for it to do. So it exits, and the threads are destroyed instantly. So let's keep the main thread alive:
import time
while True:
time.sleep(1)
Now it will keep print 'first' and 'second' until you hit Ctrl+C.
Edit: as commenters have pointed out, the daemon threads may not get a chance to clean up things like temporary files. If you need that, then catch the KeyboardInterrupt on the main thread and have it co-ordinate cleanup and shutdown. But in many cases, letting daemon threads die suddenly is probably good enough.
KeyboardInterrupt and signals are only seen by the process (ie the main thread)... Have a look at Ctrl-c i.e. KeyboardInterrupt to kill threads in python
I think it's best to call join() on your threads when you expect them to die. I've taken the liberty to make the change your loops to end (you can add whatever cleanup needs are required to there as well). The variable die is checked on each pass and when it's True, the program exits.
import threading
import time
class MyThread (threading.Thread):
die = False
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
def run (self):
while not self.die:
time.sleep(1)
print (self.name)
def join(self):
self.die = True
super().join()
if __name__ == '__main__':
f = MyThread('first')
f.start()
s = MyThread('second')
s.start()
try:
while True:
time.sleep(2)
except KeyboardInterrupt:
f.join()
s.join()
An improved version of #Thomas K's answer:
Defining an assistant function is_any_thread_alive() according to this gist, which can terminates the main() automatically.
Example codes:
import threading
def job1():
...
def job2():
...
def is_any_thread_alive(threads):
return True in [t.is_alive() for t in threads]
if __name__ == "__main__":
...
t1 = threading.Thread(target=job1,daemon=True)
t2 = threading.Thread(target=job2,daemon=True)
t1.start()
t2.start()
while is_any_thread_alive([t1,t2]):
time.sleep(0)
One simple 'gotcha' to beware of, are you sure CAPS LOCK isn't on?
I was running a Python script in the Thonny IDE on a Pi4. With CAPS LOCK on, Ctrl+Shift+C is passed to the keyboard buffer, not Ctrl+C.