I have a running variable which is responsible for whether the program is running or not. There is also a loop that runs as long as running == True. This loop contains many functions, each of which takes, say, 1 second to complete.
Thus, if during the iteration of the loop the value of running is changed to False until the iteration is completely completed, the actions will be performed.
It is necessary for me that as soon as the value of running becomes False, the cycle is interrupted immediately (well, or almost immediately).
I have this solution:
running = True
while running:
do_something1(time_length=1)
if not running:
break
do_something2(time_length=1)
if not running:
break
do_something3(time_length=1)
if not running:
break
do_something4(time_length=1)
if not running:
break
do_something5(time_length=1)
if not running:
break
do_something6(time_length=1)
if not running:
break
# etc.
However, this option looks very clumsy and takes up a lot of space. Is it possible not to prescribe a condition before each action, but to prescribe it, say, only at the beginning?
UPD 1:
Due to the fact that I did not fully show the code, the answers do not quite suit me, as I understand it.
All variables and functions are inside the class. The code itself looks like this.
from threading import Thread
class SomeClass:
def __init__(self):
self.running = True
def toggle_running_flag(self):
# this function toggles self.running by user input
self.running = not self.running
if self.running:
Thread(target=self.do_all_of_this).start()
def do_something1(self):
# do something
pass
def do_something2(self):
# do something
pass
def do_something3(self):
# do something
pass
def do_all_of_this(self):
while self.running:
self.do_something1()
if not self.running:
break
self.do_something2()
if not self.running:
break
self.do_something3()
Instead of that flag variable, you could use an exception. Note that exceptions aren't just for "bad stuff", for example the StopIteration exception is how iterators signal that they're done.
Demo:
from contextlib import suppress
class StopRunning(Exception):
pass
def do_something1():
print('do_something1')
raise StopRunning
def do_something2():
print('do_something2')
with suppress(StopRunning):
while True:
do_something1()
do_something2()
print('done')
Output:
do_something1
done
Try it online!
The is a way you can do that. You can create a function that will loop indefinitely between the functions you have to execute:
from itertools import cycle
class SomeClass:
def __init__(self):
self.running = True
def toggle_running_flag(self):
# this function toggles self.running by user input
self.running = True
Thread(target=self.do_all_of_this).start()
def do_all_of_this(self):
self.work = [self.do_something1, self.do_something2, self.do_something3]
for func in cycle(self.work):
func()
if not self.running:
return
After every iteration check if your program should still be running. If not return (Stop iteration)
Are the various do_somethings setting running = False? Relying on globals isn't a great pattern.
One alternative to updating a global flag is to have do_somethingN throw an exception to stop execution:
from do_things import StopRunning, do_something1, do_something2, # etc
try:
while True:
do_something1(time_length=1)
do_something2(time_length=1)
do_something3(time_length=1)
do_something4(time_length=1)
do_something5(time_length=1)
do_something6(time_length=1)
except StopRunning:
pass
Elsewhere:
# do_things.py
class StopRunning(Exception):
pass
def do_something1(time_length):
if time_length > 42:
raise StopRunning
# etc
Related
I am trying to replicate ES6 events behavior in python, so I wrote the following code:
from threading import Thread
from time import sleep
class Event:
def __init__(self, action):
self.action= action
self.flag = False
self.__watchFlag()
def triggerAction(self):
self.flag = True
def __watchFlag(self):
if self.flag:
self.action()
self.flag = False
Thread(target=self.__watchFlag).start()
def action():
print("Event triggered action.")
my_event = Event(action)
my_event.triggerAction()
Which works just fine except when I try to trigger the action two times consecutively, i.e:
my_event.triggerAction()
my_event.triggerAction()
Only 1 event is triggered, but if I add a sleep(1/10) in between each trigger, works well. So I am assuming there is a delay somewhere in the process, any ideas?
NOTE: PLS DON'T RECOMMEND ANY EVENTS LIBRARIES ALREADY EXISTING IN PYTHON, I KNOW THEM, THIS IS NOT MY END GOAL, THX.
If you change your code to:
def triggerAction(self):
print("setting to true")
self.flag = True
when you have:
my_event.triggerAction()
my_event.triggerAction()
you get the following output:
setting to true
setting to true
Event triggered action.
But if you have :
my_event.triggerAction()
sleep(1/10)
my_event.triggerAction()
you got:
setting to true
Event triggered action.
setting to true
Event triggered action.
So in the first case you set flag to true immediately, so you don't given enough time for the context switch to kick in.
I am really having a hard time wrapping my head around multithreading in python. My expectation of the following code is that appLoop() will run for 10 seconds and the cease to exist -- which it does when tracing through in PyCharm, but not, when I just run it. This results in an infinite loop.
import time
import multiprocessing
isRunning = True
runningSince = 0
def appLoop():
try:
global isRunning
while isRunning:
time.sleep(1)
global runningSince
runningSince = runningSince + 1
print(f'Looping since {runningSince} seconds.')
except KeyboardInterrupt:
print(f'appLoop stopped after {runningSince} seconds.')
class Process:
class __Process:
def __init__(self):
self.process = multiprocessing.Process(target=appLoop)
self.process.start()
instance = None
def __init__(self):
if not Process.instance:
Process.instance = Process.__Process()
def __del__(self):
print('Instance deleted.')
p = Process()
time.sleep(10)
isRunning = False
print(f'isRunning set to False.')
del p
This brings up (at least...) 2 questions for me:
why is process still running after del p -- am I creating a zombie process here?
why does my appLoop() keep running even after I set isRunning to false when I run the app (according to my observations this works when tracing through the code as mentioned above)?
My use case in the end is to be able to start / stop my appLoop() from a flask web interface -- which is why I am trying to implement a singleton here. Just in case you might wonder...
And: I do know that __del__ is not recommended as you never know when exactly garbage collection will call it -- in this case I just use it for (cave man) debugging.
isRunning = False changes the value of the variable in the parent process. The child process (the one that executes the while loop) has its own copy of isRunning that is not affected by the assignment.
For the same reason del p does not terminate the process: because it has its own copy of p. You should terminate the process explicitly in the destructor:
def __del__(self):
print('Instance deleted.')
Process.instance.process.terminate()
I am trying to spawn a python thread which perform a particular operation repeatedly based on a certain condition. If the condition doesn't met then the thread should exit. I have written the following code, but its running indefinitely.
class dummy(object):
def __init__(self):
# if the flag is set to False,the thread should exit
self.flag = True
def print_hello(self):
while self.flag:
print "Hello!! current Flag value: %s" % self.flag
time.sleep(0.5)
def execute(self):
t = threading.Thread(target=self.print_hello())
t.daemon = True # set daemon to True, to run thread in background
t.start()
if __name__ == "__main__":
obj = dummy()
obj.execute()
#Some other functions calls
#time.sleep(2)
print "Executed" # This line is never executed
obj.flag = False
I am new to python threading module. I have gone through some articles suggesting the use of threading.Timer() function, but this is not what I need.
The problem line is t = threading.Thread(target=self.print_hello()), more specifically target=self.print_hello(). This sets target to the results of self.print_hello(), and since this function never ends it will never be set. What you need to do is set it to the function itself with t = threading.Thread(target=self.print_hello).
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self._finished = False
self._end = False
self._running = False
def run(self):
self._running = True
while not self._finished:
time.sleep(0.05)
self._end = True
def stop(self):
if not self._running:
return
self._finished = True
while not self._end:
time.sleep(0.05)
I wish to have a thread on which I can call run() and stop(). The stop method should wait for run to complete in an orderly manner. I also want stop to return without any issues if run hasn't even be called. How should I do this?
I create this thread in a setup() method in my test environment and run stop on it in the teardown(). However, in some tests I dont call run().
UPDATE
Here's my second attempt. Is it correct now?
import threading
import time
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self._finished = False
def run(self):
while not self._finished:
print("*")
time.sleep(1)
print("Finished Other")
def finish(self):
self._finished = True
self.join()
m = MyThread()
m.start()
print("After")
time.sleep(5)
m.finish()
print("Finished Main")
You do not need to and should not implement this yourself. What you are looking for already exists, at least in large parts. It is, however, not called "stop". The concept you are describing is usually called "join".
Have a look at the documentation for join: https://docs.python.org/3.4/library/threading.html#threading.Thread.join
You write
The stop method should wait for run to complete in an orderly manner.
Join's documentation says: "Wait until the thread terminates." check ✓
You write
I also want stop to return without any issues if run hasn't even be
called
Join's documentation says: "It is also an error to join() a thread before it has been started"
So, the only thing you need to make sure is that you call join() only after you have started the thread via the start() method. That should be easy for you.
I have a threaded python application with a long-running mainloop in the background thread. This background mainloop is actually a call to pyglet.app.run(), which drives a GUI window and also can be configured to call other code periodically. I need a do_stuff(duration) function to be called at will from the main thread to trigger an animation in the GUI, wait for the animation to stop, and then return. The actual animation must be done in the background thread because the GUI library can't handle being driven by separate threads.
I believe I need to do something like this:
import threading
class StuffDoer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.max_n_times = 0
self.total_n_times = 0
self.paused_ev = threading.Event()
def run(self):
# this part is outside of my control
while True:
self._do_stuff()
# do other stuff
def _do_stuff(self):
# this part is under my control
if self.paused_ev.is_set():
if self.max_n_times > self.total_n_times:
self.paused_ev.clear()
else:
if self.total_n_times >= self.max_n_times:
self.paused_ev.set()
if not self.paused_ev.is_set():
# do stuff that must execute in the background thread
self.total_n_times += 1
sd = StuffDoer()
sd.start()
def do_stuff(n_times):
sd.max_n_times += n_times
sd.paused_ev.wait_for_clear() # wait_for_clear() does not exist
sd.paused_ev.wait()
assert (sd.total_n_times == sd.max_n_times)
EDIT: use max_n_times instead of stop_time to clarify why Thread.join(duration) won't do the trick.
From the documentation for threading.Event:
wait([timeout])
Block until the internal flag is true.
If the internal flag is true on entry,
return immediately. Otherwise, block
until another thread calls set() to
set the flag to true, or until the
optional timeout occurs.
I've found I can get the behavior I'm looking for if I have a pair of events, paused_ev and not_paused_ev, and use not_paused_ev.wait(). I could almost just use Thread.join(duration), except it needs to only return precisely when the background thread actually registers that the time is up. Is there some other synchronization object or other strategy I should be using instead?
I'd also be open to arguments that I'm approaching this whole thing the wrong way, provided they're good arguments.
Hoping I get some revision or additional info from my comment, but I'm kind of wondering if you're not overworking things by subclassing Thread. You can do things like this:
class MyWorker(object):
def __init__(self):
t = Thread(target = self._do_work, name "Worker Owned Thread")
t.daemon = True
t.start()
def _do_work(self):
While True:
# Something going on here, forever if necessary. This thread
# will go away if the other non-daemon threads terminate, possibly
# raising an exception depending this function's body.
I find this makes more sense when the method you want to run is something that is more appropriately a member function of some other class than it would be to as the run method on the thread. Additionally, this saves you from having to encapsulate a bunch of business logic inside of a Thread. All IMO, of course.
It appears that your GUI animation thread is using a spin-lock in its while True loop. This can be prevented using thread-safe queues. Based on my reading of your question, this approach would be functionally equivalent and efficient.
I'm omitting some details of your code above which would not change. I'm also assuming here that the run() method which you do not control uses the self.stop_time value to do its work; otherwise there is no need for a threadsafe queue.
from Queue import Queue
from threading import Event
class StuffDoer:
def __init__(self, inq, ready):
self.inq = inq
self.ready = ready
def _do_stuff(self):
self.ready.set()
self.stop_time = self.inq.get()
GUIqueue = Queue()
control = Event()
sd = StuffDoer(GUIqueue, control)
def do_stuff(duration):
control.clear()
GUIqueue.put(time.time() + duration)
control.wait()
I ended up using a Queue similar to what #wberry suggested, and making use of Queue.task_done and Queue.wait:
import Queue
import threading
class StuffDoer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.setDaemon(True)
self.max_n_times = 0
self.total_n_times = 0
self.do_queue = Queue.Queue()
def run(self):
# this part is outside of my control
while True:
self._do_stuff()
# do other stuff
def _do_stuff(self):
# this part is under my control
if self.total_n_times >= self.max_n_times:
try:
self.max_n_times += self.do_queue.get(block=False)
except Queue.Empty, e:
pass
if self.max_n_times > self.total_n_times:
# do stuff that must execute in the background thread
self.total_n_times += 1
if self.total_n_times >= self.max_n_times:
self.do_queue.task_done()
sd = StuffDoer()
sd.start()
def do_stuff(n_times):
sd.do_queue.put(n_times)
sd.do_queue.join()
assert (sd.total_n_times == sd.max_n_times)
I made solution based on #g.d.d.c advice for this question. There is my code:
threads = []
# initializing aux thread(s) in the main thread ...
t = threading.Thread(target=ThreadF, args=(...))
#t.setDaemon(True) # I'm not sure does it really needed
t.start()
threads.append(t.ident)
# Block main thread
while filter(lambda thread: thread.ident in threads, threading.enumerate()):
time.sleep(10)
Also, you can use Thread.join to block the main thread - it is better way.