I am working on a simple python script that sends serial communication command to an Arduino.
I have a main-thread which sends the commands and then another child-thread which waits for user input.
This should be simple but I want this process to be very fast.
For example if user inputs "stop", I want to stop the main thread immediately.
The way I was thinking of doing it was by throwing exceptions from the child thread and catch those in the main thread, but I soon realized that I was quite impossible since each thread runs on its own context and child thread can not send an exception to the main thread.
I am now trying to change the child class with attributes and check the attributes in the main thread but I will result on lots of confusing if-statements in the main thread and it is also not a good solution since it does not stop the execution immediately.
class User_input_thread (threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.isPauseRequested = False
self.isStopRequested = False
def run(self):
while (True):
s = raw_input(">")
if s.startswith("pause"):
self.isPauseRequested = True
elif s.startswith("start"):
self.isPauseRequested = False
elif s.startswith("stop"):
self.isStopRequested = True
This is the basically main thread:
def start_demo():
user_input_thread = User_input_thread()
user_input_thread.start()
while (True):
if (not user_input_thread.isPauseRequested):
# DO SOME WORK HERE
time.sleep(10)
if (not user_input_thread.isStopRequested):
# DO SOME WORK HERE
time.sleep(10)
What is an easy way to achieve the behavior I want?
Please don't link to other pages that discuss exceptions between Threads. At least explain how I should use the code or modify my code so I can understand.
EDIT (following code has been tested)
The problem with the following code is that if event takes infinite time there is no way to stop the child process.
Run the example and write "start".... then the child starts to work and then if you send another event "such as stop" it will be ignored.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import threading
import time
class child_thread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.stop_request = threading.Event()
self.start_request = threading.Event()
self.pause_request = threading.Event()
def run(self):
while 1:
if self.stop_request.isSet():
print 'stop request is set!!'
self.stop_request.clear()
while 1:
print "stop running..."
time.sleep(1)
if self.pause_request.isSet():
print 'pause request is set!!'
self.pause_request.clear()
while 1:
print "pause running..."
time.sleep(1)
if self.start_request.isSet():
print 'start request is set!!!'
self.start_request.clear()
while 1:
print "start running..."
time.sleep(1)
def show(self, event='stop'):
if event == 'stop':
self.stop_request.set()
elif event == 'start':
self.start_request.set()
elif event == 'pause':
self.pause_request.set()
elif event == 'exit':
print 'enter exit'
self.join(1)
def main_thread():
t1 = child_thread()
t1.start()
while 1:
s = raw_input(">")
t1.show(event=s)
if s == 'exit':
break
print 'Done! child thread has been killed'
main_thread()
It's not a best practice to for a child thread to control its parent thread. We usually do the other way around.
In your case, you can put user input in the main thread and put your work in the child thread.
Communication between threads in Python is usually done by Event and signal. You can google this if you show interests.
Here is an example for your case. Hope it helps. I simplify the if conditions which is not the key point here. :)
#!/usr/bin/python
# -*- coding: utf-8 -*-
import threading
import time
class child_thread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.stop_request = threading.Event()
self.start_request = threading.Event()
self.pause_request = threading.Event()
def run(self):
while 1:
if self.stop_request.isSet():
print 'stop request is set!!'
break
if self.pause_request.isSet():
print 'pause request is set!!'
break
if self.start_request.isSet():
print 'start request is set!!!'
break
print 'no stop/pause/start is set!!'
time.sleep(1)
def join(self, timeout=None, event='stop'):
if event == 'stop':
self.stop_request.set()
elif event == 'start':
self.start_request.set()
elif event == 'pause':
self.pause_request.set()
super(child_thread, self).join(timeout)
def main_thread():
t1 = child_thread()
t1.start()
while 1:
s = raw_input(">")
t1.join(event=s)
break
print 'Done! chile thread has been killed'
main_thread()
And here is a very good article for communications between threads in Python. http://eli.thegreenplace.net/2011/12/27/python-threads-communication-and-stopping
EDIT:
Here is code for your updated request.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import threading
import time
class child_thread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.stop_request = threading.Event()
self.start_request = threading.Event()
self.pause_request = threading.Event()
def run(self):
while 1:
if self.stop_request.isSet():
print 'stop request is set!!'
self.stop_request.clear()
continue
if self.pause_request.isSet():
print 'pause request is set!!'
self.pause_request.clear()
continue
if self.start_request.isSet():
print 'start request is set!!!'
self.start_request.clear()
continue
def show(self, event='stop'):
if event == 'stop':
self.stop_request.set()
elif event == 'start':
self.start_request.set()
elif event == 'pause':
self.pause_request.set()
elif event == 'exit':
print 'enter exit'
self.join(1)
def main_thread():
t1 = child_thread()
t1.start()
while 1:
s = raw_input("\n>enter exit to exit:")
t1.show(event=s)
if s == 'exit':
break
print 'Done! chile thread has been killed'
main_thread()
Related
I'm trying to update the label to display the numbers being counted down when it receives the number through the queue. I am able to see it being printed in the console but the label does not change. Any help or suggestion would be helpful!
Here is my code:
import tkinter as tk
import time
import threading
import queue
class GUIApp:
def __init__(self):
self.root = tk.Tk()
self.buttonCountDown = tk.Button(text='Count Down', command=self.countDownAction)
self.buttonCountDown.pack()
self.label = tk.Label(text='default')
self.label.pack()
self.queue = queue.Queue()
self.root.mainloop()
def countDown(self, seconds):
for i in range(seconds, 0, -1):
self.queue.put(i)
time.sleep(1)
def listenToQueue(self):
while True:
try:
if self.queue.empty() == False:
print(self.queue.get(0))
self.label['text'] = self.queue.get(0)
elif self.queue.empty() == True:
pass
except queue.Empty:
pass
def countDownAction(self):
listenThread = threading.Thread(target=self.listenToQueue)
listenThread.start()
thread = threading.Thread(target=self.countDown, args=(5,))
thread.start()
thread.join()
app = GUIApp()
The first thing you need to know is Queue.get() removes the item and returns it, similar to dict.pop(). So when you do print(self.queue.get(0)), the item is already removed from the queue. You have to assign it to a variable first if you want to both print and config it:
def listenToQueue(self):
while True:
try:
if self.queue.empty() == False:
s = self.queue.get(0)
print (s)
self.label['text'] = s
elif self.queue.empty() == True:
pass
except queue.Empty:
pass
Next, calling thread.join() will wait for the thread terminates. You don't need to call this method at all in your current setup.
def countDownAction(self):
listenThread = threading.Thread(target=self.listenToQueue)
listenThread.start()
thread = threading.Thread(target=self.countDown, args=(5,))
thread.start()
#thread.join() #not required
my script works like this:
# other part of code
class request(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
while True:
try:
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.connect((host), (port))
socket.send(str.encode("test"))
except:
socket.close()
def loop():
for x in range(5):
request(x).start()
# other
# part
# of code
def startall():
# some other code
choice = input("command: ")
if choice == "request":
loop()
elif choice == "stop":
# ?
# some other code
startall()
Is there a way to stop sending request if the input is "stop"? Note that this is just a sample, my script doesn't work like this. I put this code just to let you understand what is my problem
If you want to stop all requests at once you can modify your class as folows:
class request(threading.Thread):
REQUESTS_ALLOWED = True
def run(self):
while request.REQUESTS_ALLOWED:
socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
socket.connect((host), (port))
socket.send(str.encode("test"))
except:
pass # Do what you need
finally:
socket.close()
Notice the alternation of closing socket. In your code the socket was closed when garbage collector destroyed your variable socket. With my alternation it's guaranteed that socket is closed with every iteration.
The start and stop events now can change the state of all request objects.
if choice == "request":
request.REQUESTS_ALLOWED = True
loop()
elif choice == "stop":
request.REQUESTS_ALLOWED = False
After setting REQUESTS_ALLOWED to False you should join() all running threads. It's just recommendation (and you don't have to do it) because normally when function returns it indicates that something is done. So after return from function startall() with choice = "stop" I would expect that all started threads are stopped.
Full code example:
import threading
import time
class Request(threading.Thread):
REQUESTS_ALLOWED = True
active_threads = set()
def __init__(self):
threading.Thread.__init__(self)
def start(self):
Request.active_threads.add(self) # Add thread to set for later use
super().start()
def run(self):
while Request.REQUESTS_ALLOWED:
print("Thread {} is alive.".format(self.name))
time.sleep(1)
print("Thread {} is done.".format(self.name))
def loop():
for x in range(5):
Request().start()
def startall(choice):
if choice == "request":
Request.REQUESTS_ALLOWED = True
loop()
elif choice == "stop":
Request.REQUESTS_ALLOWED = False
# Iterate through active threads and wait for them
for thread in Request.active_threads:
thread.join()
Request.active_threads.clear()
startall("request")
time.sleep(3)
startall("stop")
The code was tested in Python 3.6.1
I am using a Queue for communicating between processes and also an event flag to indicate whether or not the parent process wants to exit however the Queue in child process is in a blocked state, waiting for more input.
I can make the queue not block by using get_nowait() however that is making the processor use 100%
What is the recommended way of being able to close a child process and accept input at the same time?
from multiprocessing import Process
from multiprocessing import Event
from multiprocessing import Queue
class EchoProcess(Process):
def __init__(self, iQ, closeEvent):
Process.__init__(self)
self.iQ = iQ
self.closeEvent = closeEvent
def run(self):
while not self.closeEvent.is_set():
istring = self.iQ.get()
print(istring)
print("exited")
if __name__ == "__main__":
iQ = Queue()
closeEvent = Event()
echoProcess = EchoProcess(iQ, closeEvent)
echoProcess.start()
while True:
istring = raw_input("Enter:")
if istring == "quit": break
iQ.put(istring)
closeEvent.set()
You can use a sentinel.
Define the sentinel as a value that never appears in normal data and when EchoProcess gets it, it quits.
For example:
from multiprocessing import Process
from multiprocessing import Queue
class Sentinel(object): pass
class EchoProcess(Process):
def __init__(self, iQ):
Process.__init__(self)
self.iQ = iQ
def run(self):
for istring in iter(iQ.get, Sentinel):
print(istring)
print("exited")
if __name__ == "__main__":
iQ = Queue()
echoProcess = EchoProcess(iQ)
echoProcess.start()
while True:
istring = raw_input("Enter:")
if istring == "quit": break
iQ.put(istring)
iQ.put(Sentinel)
echoProcess.join()
I have simple script for watchdog on network device. Script monitors response from PING command. If there is no answer then second thread executes and first thread is stopped. If second thread is finished then first thread is resumed (checking ping). If there is no answer then following message appears:
RuntimeError: threads can only be started once
Here is my code:
#!/usr/bin/python
import os
import time
import sqlite3
from ablib import Pin
import threading
led=Pin('W9','OUTPUT')
class threadout1(threading.Thread):
def run(self):
while True:
conn = sqlite3.connect('database/database.db')
cur = conn.cursor()
cur.execute("SELECT * FROM watchdog")
rows_output = cur.fetchall()
time.sleep(1)
if rows_output[0][1] == "ping":
response = os.system("ping -c 1 " + rows_output[0][2])
if response != 0:
print "bad"
rest.start()
rest.join()
class restart(threading.Thread):
def run(self):
led.on()
time.sleep(15)
led.off()
thr = threadout1()
rest = restart()
thr.start()
You can either create the restart thread every time you need it
if response != 0:
print "bad"
restart_thread = restart()
restart_thread.start()
restart_thread.join()
or use Events
class restart_thread(threading.Thread):
def __init__(self, evt):
self.evt = evt
def run(self):
self.evt.wait()
# do stuff
self.evt.clear()
class threadout(threading.Thread):
def __init__(self, evt):
self.evt = evt
def run(self):
if #other thread needs to run once
self.evt.set()
evt = threading.Event()
restart_thread = restart(evt)
restart_thread.start()
pinging_thread = threadout(evt)
pinging_thread.start()
To make the pinging_thread wait for the restart_thread to finish, you could use another Event.
The following code executes two threads (multithread), each with different time delays so that each thread will finish at a different time.
Once both threads are finished module display1.py issues a print statement saying they are BOTH finished.
I would like module display1.py to issue a 'finished' statement for EACH thread AS EACH thread finishes
How can i do this ... amendments to my working code appreciated! I'd like to change as little of the current code as possible so a better form of variable transfer between the two modules might be what I'm after
display1.py
from threads1 import *
manager = ThreadManager()
manager.start(False)
print (manager.GetResults())
threads1.py
from threading import Thread
import time
class ThreadManager:
def __init__(self):
pass
def start(self, answer):
self.answer = answer
thread_refs = []
t1 = MyThread(70, 'Not finished')
t1.daemon = True
t1.start()
t2 = MyThread(2, 'Not finished')
t2.daemon = True
t2.start()
while True:
if t1.AskFinished == 'Finished' and t2.AskFinished == 'Finished': #If I break the loop after EACH site, Only the first to finish will be sent via GetResults to display1.py
global results
results = [t1.AskFinished, t2.AskFinished]
print("Both Finished")
break
def GetResults(self):
global results
return(results)
class MyThread(Thread):
def __init__(self, SleepWait, AskFinished):
Thread.__init__(self)
self.SleepWait = SleepWait
self.AskFinished = AskFinished
def run(self):
time.sleep(self.SleepWait)
self.AskFinished = 'Finished'
What you have here (entering a very tight check loop in the main thread) is a very naive approach to threading in many languages, but especially in python where GIL contention will just slow the threads down a great bit.
What is a better idea is instead using queue.Queue to push info when a thread is completed. This allows the main thread to block on the queue instead, which is less CPU intensive as well as allowing you to know (out of order) which one is finished.
The changes you would need to make:
at the top of the module threads1.py:
import queue
finished_queue = queue.Queue()
in your start():
num_finished = 0
while True:
info = finished_queue.get()
num_finished += 1
if info is t1:
print("t1 finished")
else:
print("t2 finished")
if num_finished == 2:
global results
results = [t1.AskFinished, t2.AskFinished]
print("Both Finished")
break
and finally in run():
def run(self):
time.sleep(self.SleepWait)
self.AskFinished = 'Finished'
finished_queue.put(self)
Some more fundamental modifications I'd make is actually pushing the result into the queue and then fetching the results out, skipping the extra step before GetResults. Furthermore, if GetResults had to stay, I'd pass them through a field on self e.g. self.results = [t1.AskFinished, t2.AskFinished]
Update:
Ok, so you want to know more about how to have display1.py print the results. It would be helpful if you could explain why it matters, because that might make a difference in how you should do this, but here's a first approach:
# threads1.py
from threading import Thread
import time
class ThreadManager:
def __init__(self):
self.threads = {}
def start(self):
t1 = MyThread(4)
t1.daemon = True
t1.start()
self.threads[1] = t1
t2 = MyThread(1)
t2.daemon = True
t2.start()
self.threads[2] = t2
def is_alive(self, thread_id):
return self.threads[thread_id].is_alive()
def GetResults(self): # or you could just access results directly
return self.results
class MyThread(Thread):
def __init__(self, SleepWait):
Thread.__init__(self)
self.SleepWait = SleepWait
def run(self):
time.sleep(self.SleepWait)
And then...
# display1.py
from threads1 import *
manager = ThreadManager()
manager.start()
t1_state = t2_state = True
while manager.is_alive(1) or manager.is_alive(2):
time.sleep(1)
if manager.is_alive(1) != t1_state:
print("t1 finished")
t1_state = manager.is_alive(1)
if manager.is_alive(2) != t2_state:
print("t2 finished")
t2_state = manager.is_alive(2)
if not manager.is_alive(1) and not manager.is_alive(2):
print("Both Finished")
break
You should eventually consider using a Queue as suggested by Crast; but let's focus on getting this right first.
Original Post:
There are a number of problems with this code.
First, you should use t1.is_alive() to check if a thread is finished. There's no need to reimplement it with AskFinished.
Second, the while True: loop in threads1.py is doing nothing at a furious rate while it waits for your threads to terminate. Take a look at the cpu usage while this is running if you don't believe me. You should throw a time.sleep(1) statement in there.
Third, why are you using a global var to return your results? That's a really strange thing to do. Just store it in self!
And finally, why does display1.py have to print the messages? Why can't thread1.py do that?
With these four points in mind, here's a thread1.py that works more sensibly:
from threading import Thread
import time
class ThreadManager:
def __init__(self):
self.results = None
def start(self, answer): # why is "answer" here?
self.answer = answer
thread_refs = []
t1 = MyThread(4, 'Not finished')
t1.daemon = True
t1.start()
t2 = MyThread(1, 'Not finished')
t2.daemon = True
t2.start()
t1_state = t2_state = True
while t1.is_alive() or t2.is_alive():
time.sleep(1)
if t1.is_alive() != t1_state:
print("t1 finished")
t1_state = t1.is_alive()
if t2.is_alive() != t2_state:
print("t2 finished")
t2_state = t2.is_alive()
if not t1.is_alive() and not t2.is_alive():
self.results = [t1.AskFinished, t2.AskFinished]
print("Both Finished")
break
def GetResults(self): # or you could just access results directly
return self.results
class MyThread(Thread):
def __init__(self, SleepWait, AskFinished):
Thread.__init__(self)
self.SleepWait = SleepWait
self.AskFinished = AskFinished
def run(self):
time.sleep(self.SleepWait)
self.AskFinished = 'Finished'
Now, this still doesn't do exactly what you wanted, because you asked for display.py to do the displaying. To make that work, you'd have to put your while True loop in display.py and add an ThreadManager.is_alive() method that display.py could use to check whether a thread is alive or not. If you want to see how to do that let me know.
Im not familiar with threading but since no answers yet ill give it a shot.
In this:
Cant you just add two if statements before hand?
while True:
if t1.askFinished == 'Finished':
print("t1 Finished")
if t2.askFinished == 'Finished':
print("t2 Finished")
if t1.AskFinished == 'Finished' and t2.AskFinished == 'Finished': #If I break the loop after EACH site, Only the first to finish will be sent via GetResults to display1.py
global results
results = [t1.AskFinished, t2.AskFinished]
print("Both Finished")
break
edit: I tried changing your code as little as possible... it's not very well written though tbh. (No offense)