Exit Several Threads on Quit Command - python

I have a thread which adds commands inputted on the command line to a command queue. One of those commands is a command to quit the program.
def cli(cmd_queue):
while True:
i = input()
if i == "q":
cmd_queue.put(("QUIT",))
return
cmd_queue = queue.Queue()
cli_thread = threading.Thread(target=cli, args=(cmd_queue,), daemon=True)
cli_thread.start()
I could end another thread like this:
def some_thread():
while True:
while not cmd_queue.empty():
cmd = cmd_queue.get()
if cmd == ("QUIT",): return
do_stuff()
time.sleep(0.001)
But what might I do if I have several threads in my program, and I wish for all of them to break out of while True loops?
UPDATE
The threads I wish to break out of have different algorithms, for example:
def some_thread_1():
while True:
while not cmd_queue.empty():
cmd = cmd_queue.get()
if cmd == ("QUIT",): return
do_stuff()
time.sleep(0.001)
def some_other_thread_2():
while True:
while not cmd_queue.empty():
cmd = cmd_queue.get()
if cmd == ("QUIT",): return
do_other_stuff()
time.sleep(0.001)

As the comments suggested, put a sentinel value for each thread to signal it to exit. Here's an example. There's no possibility of a thread getting two sentinels because they stop drawing from the queue once they get one.
from threading import Thread
from queue import Queue
import time
q = Queue()
# Factory function for creating functions with fixed multipliers of input data.
def mult_factory(n):
def mult(n=n):
while True:
data = q.get()
if data is None: # Sentinel value?
break
print(f'{data} * {n} = {data * n}')
print(f'Quitting mult{n}')
return mult
# Create 5 threads with different multiplication factors.
threads = [Thread(target=mult_factory(n)) for n in range(5)]
for t in threads:
t.start()
# Generate some work
for i in range(10):
q.put(i)
# Add a "done" for each thread.
for t in threads:
q.put(None)
for t in threads:
t.join()
Output:
0 * 1 = 0
1 * 3 = 3
3 * 4 = 12
7 * 4 = 28
8 * 4 = 32
9 * 4 = 36
6 * 3 = 18
5 * 1 = 5
Quitting mult4
Quitting mult3
Quitting mult1
2 * 0 = 0
4 * 2 = 8
Quitting mult0
Quitting mult2

Related

How to synchronize threads with shared global variable in python?

i'm currently trying to unterstand threading in python and i wrote a program that ideally would have 2 threads alternating between incrementing and decrementing a global variable but no matter how i spread out the lock it inevitably becomes out of sync.
number = 0
lock = threading.Lock()
def func1():
global number
global lock
while True:
try:
lock.acquire()
number += 1
finally:
lock.release()
print(f"number 1 is: {number}")
time.sleep(0.1)
def func2():
global number
global lock
while True:
try:
lock.acquire()
number -= 1
finally:
lock.release()
print(f"number 2 is: {number}")
time.sleep(0.1)
t1 = threading.Thread(target=func1)
t1.start()
t2 = threading.Thread(target=func2)
t2.start()
t1.join()
t2.join()
the output should look something like this:
number 1 is: 1
number 2 is: 0
number 1 is: 1
number 2 is: 0
number 1 is: 1
number 2 is: 0
number 1 is: 1
number 2 is: 0
but right now it looks like this:
number 1 is: 1
number 2 is: 0
number 1 is: 1
number 2 is: 0
number 2 is: -1number 1 is: 0
number 2 is: -1number 1 is: 0
number 1 is: 1number 2 is: 0
any idea how to do this without falling out of sync?
First, avoid using global variables with threads in python. Use a queue to share the variables instead.
Second, the lock acquisition in non-deterministic. At the moment a lock is released, you have no guarantee that the other thread will grab it. There is always a certain probability that the thread that just released the lock can grab it again before the other thread.
But in your case, you can avoid problems because you know the state that the variable needs to be to accept modifications by one thread or the other. So, you can enforce the protection for modification by verifying if the variable is in the right state to accept a modification.
Something like:
from threading import Thread
import time
from queue import Queue
def func1(threadname, q):
while True:
number = q.get()
if number == 0:
number += 1
print(f"number 1 is: {number}")
q.put(number)
time.sleep(0.1)
def func2(threadname, q):
while True:
number = q.get()
if number == 1:
number -= 1
print(f"number 2 is: {number}")
q.put(number)
time.sleep(0.1)
queue = Queue()
queue.put(0)
t1 = Thread(target=func1, args=("Thread-1", queue))
t2 = Thread(target=func2, args=("Thread-2", queue))
t1.start()
t2.start()
t1.join()
t2.join()
thanks for all your answers, i remember seing someone in the comments mentioned using events or something like that and that solved the issue. here's the code:
number = 0
event_number = threading.Event()
event_number.clear()
def func1():
global number
global event_number
while True:
if not event_number.is_set():
number += 1
print(f"func 1 is {number}")
event_number.set()
else:
pass
time.sleep(2)
def func2():
global number
global event_number
while True:
if event_number.is_set():
number -= 1
print(f"func 2 is {number}")
event_number.clear()
else:
pass
time.sleep(2)
t1 = threading.Thread(target=func1)
t2 = threading.Thread(target=func2)
t1.start()
t2.start()
t1.join()
t2.join()
now i notice that sometimes one of the loops will either not wait it's alloted time and print right away or wait double the time but at least the number only stays within those 2 values.
For starters, time.sleep is not exactly accurate. And depending on the python-implementation you're using (most likely cpython) multithreading might not quite work the way you're expecting it to. These two factors allow the initially correct timing of your threads to get out of sync within fairly short time.
There solution for this problem is to enforce alternate operation on the variable by the two threads via two locks:
import time
import threading
var = 0
def runner(op, waitfor, release):
global var
while True:
try:
# wait for resource to free up
waitfor.acquire()
# operation
var = op(var)
print(f"var={var}")
finally:
# notify other thread
release.release()
time.sleep(0.1)
# init locks for thread-synchronization
lock_a = threading.Lock()
lock_b = threading.Lock()
lock_a.acquire()
lock_b.acquire()
# create and start threads (they'll wait for their lock to be freed)
thread_a = threading.Thread(target=runner, args=(lambda v: v - 1, lock_a, lock_b))
thread_b = threading.Thread(target=runner, args=(lambda v: v + 1, lock_b, lock_a))
thread_a.start()
thread_b.start()
# let thread_b start the first operation by releasing the lock
lock_b.release()
In the above code, each thread has a lock that can be used to notify it, that the resource may be used by it. Thus threads can hand control over the global variable to each other.

Threading with priority queue

I wanted to test threads in python, so I tried to create 3 threads that run at the same time but each one prints a different letter from A - C.
It was easy so I added that the letters would be printed from first to last and I'm getting confused from here.
How can I make 3 threads that run talk to each other and to know when to print and when to not print?
the output should be:
A B C A B C A B C A B C
my code:
import threading
import time
import queue
val = 0
def increment(letter):
global val
for x in range(100):
val += 1
lock.acquire()
time.sleep(0.5)
print(val, letter, ' ')
lock.release()
lock = threading.Lock()
thread1 = threading.Thread(target=increment, args=('A',))
thread2 = threading.Thread(target=increment, args=('B',))
thread3 = threading.Thread(target=increment, args=('C',))
thread1.start()
thread2.start()
thread3.start()

How to measure time taken of multi-threads created in a loop?

I want to measure how much time it takes to finish running the code with multiple threads in python.
If I put join inside the loop, it will stop the loop (main thread) from keep creating new threads. It will run the sleep() one by one.
If I put join on the thread which I use to create thread_testing, the join won't work somehow. It prints out the time immediately.
def sleep(name):
print("{} going to sleep".format(name))
time.sleep(5)
print("{} wakes up after 5 seconds".format(name))
def thread_testing():
for i in range(3):
t = threading.Thread(target=sleep, name='thread' + str(i), args=(i,)
t.start()
# t.join() #1
if __name__ == '__main__':
start = time.time()
t = threading.Thread(target=thread_testing, name='threadx')
t.start()
t.join() #2
print(time.time() - start)
Desired output:
1 sleep
2 sleep
3 sleep
1 wake up after 5
2 wake up after 5
3 wake up after 5
5.xxx secs
Join will wait for your thread. That is why your threads were executed one by one.
What you have to do is:
Start all threads
Store them somewhere
Once everything is started wait for every thread to finish.
Assuming you don't need the first thread started in main:
import time
import threading
def sleep(name):
print("{} going to sleep".format(name))
time.sleep(5)
print("{} wakes up after 5 seconds".format(name))
def thread_testing():
threads = []
for i in range(3):
t = threading.Thread(target=sleep, name='thread' + str(i), args=(i,))
t.start()
threads.append(t)
for t in threads:
t.join()
if __name__ == '__main__':
start = time.time()
thread_testing()
print(time.time() - start)

How to prevent print() lift up input text while user is typing

Basically, I have a async worker doing stuff, but I can caught an user command line any time.
The problem is just visual, when the user is typing and the code prints something, the text the user was typing is lifted up together with the printed text. How to make the last line "isolated" from console?
this is an exemple code:
import queue
import threading
import time
import heapq
def worker():
while True:
item = q.get()
if item is None:
break
do_work(item)
#q.task_done()
time.sleep(2)
q.put(item)
def do_work(item):
print(item,end = '')
print(time.time(), end = '')
q = queue.PriorityQueue()
num_worker_threads = 1
threads = []
for i in range(num_worker_threads):
t = threading.Thread(target=worker)
t.start()
threads.append(t)
fruits = [(1,"apple"), (2,"banana"), (3,"cherry")]
for x in fruits:
q.put(x)
gameFinished = 0
# block until all tasks are done
#q.join()
while not gameFinished:
q.put((-1,input()))
# stop workers
for i in range(num_worker_threads):
q.put(None)
for t in threads:
t.join()
input("Press enter to exit ;)")

Python multi-threading two parallel loops

Let says I have two parallel block loops. What is the best way to run them in parallel using python. Currently I am experimenting with multi-threading using following program
#!/usr/bin/env python
import time
import serial
import os
from threading import Thread
ser = serial.Serial(port='/dev/ttyUSB0', baudrate=38400, timeout=None)
ser.flushInput()
ser.flushOutput()
def getstrings(port):
buf = bytearray()
while True:
b = port.read(1)
if b == b'\x02':
del buf[:]
elif b == b'\x03':
yield buf.decode('ascii')
else:
buf.append(b)
def tester():
while 1:
print('testing')
def values():
count = ""
tem = ""
hv = ""
counti = 0
temi = 0
hvi = 0
while 1:
for item in getstrings(ser):
#if len(item) >= 10:
# continue
if item[1] == "C":
count = item.split('C')[1]
counti=int(count[0:5])
if item[1] == "T":
tem = item.split('T')[1]
temi=int(tem[0:5])
if item[1] == "H":
hv = item.split('H')[1]
hvi = int(hv[0:5])/10
print ("HV="+str(hvi)+" "+"Count="+str(counti)+" "+"Temp="+str(temi))
t1 = Thread(target=values)
t2 = Thread(target=tester)
t1.start()
t2.start()
Only the second thread works. It doesn't print the values from second. This is the first time I am experimenting with multi-threading. Once, I understood how this will function then I intend to use this to design a GUI using Tkinter libraries. I want to use loop of my program along Tkinter main loop. Any suggestion where I might be making a mistakes.
Update:
Yes it thread 2 not thread 1. My mistakes sorry about that. But individually both threads work if I comments t1.start() or t2.start(). However, together only thread 2 prints the output.

Categories

Resources