Why is helper thread not exiting in this python script? - python

I have three files:
helper.py
from globalvariables import *
global exit_signal
global exited_signal
def helperstart():
global exited_signal
while True:
if exit_signal is True:
exited_signal = True
print ('Exiting from helper thread')
return
main.py
from globalvariables import *
import threading
import helper
global exit_signal
global exited_signal
def mainstart():
global exit_signal
helper_thread = threading.Thread(target = helper.helperstart)
input ('Press <enter> to start the thread')
helper_thread.start()
input ('Press <enter> to end the thread')
exit_signal = True
# check if helper has exited
if exited_signal is True:
print ('Helper exited successfully')
if __name__ == '__main__':
mainstart()
globalvariables.py
exit_signal = False
exited_signal = False
From main.py, the value of exit_signal should be edited to True. This should make the helper thread exit. But it is not exiting. I've tried printing the value of exit_signal from helperstart() function and it keeps showing as False. So, the main.py isn't editing the variable properly. Please help me figure out why.

Related

How to ensure there is no runaway thread

import threading
from playsound import playsound
import time
soundList = []
bot_run = True
class myThread (threading.Thread):
def __init__(self):
global soundList
threading.Thread.__init__(self)
def run(self):
global soundList
global bot_run
while bot_run:
try:
time.sleep(1)
playsound(soundList.pop(0))
except:
pass
def main():
global soundList
global bot_run
input()
soundThread = myThread()
soundThread.start()
while not ((user_input := input('sound name: ')) == 'close'):
soundList.append(user_input)
bot_run = False
main()
I want to ensure someone who clicks the x on cmd doesn't end the program but the thread keeps going.
Does the thread die on its own when the main program ends? Does the bot_run variable cease to exist and therefore exit the while loop? How can I make sure I don't have this loop running forever when it's no longer being used if the user exited the program without following procedure?

How to keep executing code until another function returns value?

from time import sleep
def foo():
sleep(3)
return True
while True:
print('Running')
if foo() == True:
print('Finished.')
break
I want to keep printing "Running" but when foo returns True I want to print "Finished" (once) and break out of the loop.
I have tried the above but it prints "Running" just once and waits for foo to finish executing and then continues.
import threading
from time import sleep
flag = True
def foo()->None:
global flag
sleep(1)
flag = False
if __name__ == "__main__":
t1 = threading.Thread(target=foo)
t1.start()
while flag:
print('Running')
print('Finished')
Because you worked with only one thread, when you call the function the main stops until the function returns.
Therefore, if you want the main code and the function to run together, you have to work with threads.
So, after trying somethings I found 2 solutions, of my own question, that uses threading.
1. Modifies the foo function
from time import sleep
from threading import Thread
x = True
def foo():
sleep(3)
global x
x = False
print('finished')
def printing():
while x:
print('Running')
foo_thread = Thread(target=foo)
foo_thread.start()
printing_thread = Thread(target=printing)
printing_thread.start()
2. Uses decorator to keep foo unchanged
from time import sleep
from threading import Thread
x = True
def sets_true(func):
def wrapper():
returned_value = func()
global x
x = False
print('finished')
return wrapper
#sets_true
def foo():
sleep(3)
return True
def printing():
while x:
print('Running')
foo_thread = Thread(target=foo)
foo_thread.start()
printing_thread = Thread(target=printing)
printing_thread.start()

Python exit a threading function from inside another function

I want to exit a threading function from inside another function. Here is an example:
from threading import *
from time import *
import keyboard
def ExampleFunction():
while True:
print('The Example function is running.') # do something in a loop
sleep(1)
def ExitFunction():
print('ExitFunction was called.')
keyboard.wait('f8') # wait until f8 was pressed
# do whatever it takes to exit the ExampleFunction
if __name__ == '__main__':
Thread(target=ExampleFunction).start()
Thread(target=ExitFunction).start()
You could use global variable running = True and in ExampleFunction use while running: and in other function use running = False
from threading import *
from time import *
import keyboard
# global variable
running = True
def ExampleFunction():
print("ExampleFunction: start")
while running:
#print('The Example function is running.') # do something in a loop
sleep(1)
print("ExampleFunction: exit")
def ExitFunction():
global running # inform function to use global variable instead of local variable
print('ExitFunction: start')
keyboard.wait('f8') # wait until f8 was pressed
running = False
print('ExitFunction: sleep')
sleep(5) # simulate other code
print('ExitFunction: end')
if __name__ == '__main__':
t1 = Thread(target=ExampleFunction)
t1.start()
t2 = Thread(target=ExitFunction)
t2.start()
t1.join() # wait for end of `ExampleFunction`
t2.join() # wait for end of `ExitFunction`
The same way you can use threading.Event and it doesn't need to use global running because it will not use = inside ExitFunction
from threading import *
from time import *
import keyboard
# global variable
running = Event()
running.set() # set `True`
def ExampleFunction():
print("ExampleFunction: start")
while running.is_set():
#print('The Example function is running.') # do something in a loop
sleep(1)
print("ExampleFunction: exit")
def ExitFunction():
print('ExitFunction: start')
keyboard.wait('f8') # wait until f8 was pressed
running.clear() # set `False`
print('ExitFunction: sleep')
sleep(5) # simulate other code
print('ExitFunction: end')
if __name__ == '__main__':
t1 = Thread(target=ExampleFunction)
t1.start()
t2 = Thread(target=ExitFunction)
t2.start()
t1.join() # wait for end of `ExampleFunction`
t2.join() # wait for end of `ExitFunction`
You may even send Event as argument in Thread
from threading import *
from time import *
import keyboard
# global variable
running = Event()
running.set() # set `True`
def ExampleFunction(running):
print("ExampleFunction: start")
while running.is_set():
#print('The Example function is running.') # do something in a loop
sleep(1)
print("ExampleFunction: exit")
def ExitFunction(running):
print('ExitFunction: start')
keyboard.wait('f8') # wait until f8 was pressed
running.clear() # set `False`
print('ExitFunction: sleep')
sleep(5) # simulate other code
print('ExitFunction: end')
if __name__ == '__main__':
t1 = Thread(target=ExampleFunction, args=(running,)) # `args` needs `,` inside `()` to create `tuple`
t1.start()
t2 = Thread(target=ExitFunction, args=(running,)) # `args` needs `,` inside `()` to create `tuple`
t2.start()
t1.join() # wait for end of `ExampleFunction`
t2.join() # wait for end of `ExitFunction`
BTW:
If you would use multiprocessing then you could kill() other process.

Why the thread does not stops?

The thread started in the start_thread method does not stop. Why ?
import time
import threading
cont_running = True
def start_thread():
threading.Thread(target=run).start()
def stop_thread():
cont_running = False
def run():
while cont_running:
print 'Thread running : ' + str(cont_running)
time.sleep(0.2)
print 'Thread ended'
start_thread()
time.sleep(2)
stop_thread()
In stop_thread(), your assignment statement creates a local variable named cont_running. This local variable is unrelated to the global variable of the same name.
Try this:
def stop_thread():
global cont_running
cont_running = False

Python - Polling a variable

I change a global variable in a signal handler and poll for it in the main program. But the value does not change in the main thread.
Is there a qualifier that I need to use to make it a volatile (like in Java) variable?
Here's the program:
test.py
import time
import signal
def debug():
closeSession = False
def sigint_handler(signal, frame):
global closeSession
print('Breaking the poll...')
closeSession=True
signal.signal(signal.SIGINT, sigint_handler)
# Start a program...
while not closeSession:
time.sleep(1)
print('Polling... closeSession = %r' % closeSession)
print('Exiting! Bye.')
# Sent 'quit' to stdin of the program
if __name__ == "__main__":
debug()
sigint_handler() gets called whenever I press Ctrl + C but the new value of closeSession is not used in the main thread.
I get the following output:
$ python test.py Polling... closeSession = False Polling...
closeSession = False
I press Ctrl + C
^CBreaking the poll... Polling... closeSession = False
Press Ctrl + C, again
^CBreaking the poll... Polling... closeSession = False
Press Ctrl + C, again
^CBreaking the poll... Polling... closeSession = False
Polling... closeSession = False
The problem is scope.
Inside the debug() function, you didn't declare closeSession as a global, which means that you have two variables called closeSession. One global and one scoped within the debug() function. And inside the sigint_handler() function, you've explicitly instructed to use global one, which is shadowed by the scoped one in the outer function.
You can solve this by declaring global before assignment in debug():
def debug():
global closeSession
closeSession = False
...
By the way, your code does not work on windows, it throws a IOError because the sleep function is interrupted. A workaround that worked for me is:
...
while not closeSession:
try:
time.sleep(1)
except IOError:
pass
print('Polling... closeSession = %r' % closeSession)
...
It's not pretty but it works.
You have to set global closeSession before accessing the variable, else you're creating a local variable with the same name and the loop will never end.
Try this:
import time
import signal
def debug():
global closeSession # <-- this was missing
closeSession = False
def sigint_handler(signal, frame):
global closeSession
print('Breaking the poll...')
closeSession=True
signal.signal(signal.SIGINT, sigint_handler)
# Start a program...
while not closeSession:
time.sleep(1)
print('Polling... closeSession = %r' % closeSession)
print('Exiting! Bye.')
# Sent 'quit' to stdin of the program
if __name__ == "__main__":
debug()

Categories