I want to execute a task after certain time, so I have tried a countdown timer with a condition of being finished (when countdown variable = 0, the task is performed). The thing is that I don't want to stop the execution of the main program while performing the countdown. I have tried this:
import time
def countdown(num_of_secs):
while(num_of_secs):
time.sleep(1)
num_of_secs -= 1
return num_of_secs
So, I run my code setting a number of seconds to the countdown, and when this countdown reaches the 0 value, a task must be executed. Using this code (it uses a while), when I call my function "countdown" it stops the execution of the main program, so it is the same as a big time.sleep. I want to carry out this countdown in the background, without stopping other actions until the countdown finishes and the task starts.
Thank you
Another alternative is by using threading.
I've got a simple example here with 2 Threads where the working thread is waiting for the countdown thread to finish and starting. The Main is still working fine.
import threading
import time
def do_something():
countdown_thread.join()
print("Starting Task")
time.sleep(3)
print("Finished Task")
def countdown(num_of_secs):
while(num_of_secs):
time.sleep(1)
num_of_secs -= 1
print(num_of_secs)
if __name__ == '__main__':
countdown_thread = threading.Thread(target=countdown, args=(3,))
work_thread = threading.Thread(target=do_something)
countdown_thread.start()
work_thread.start()
while True:
print("Main doing something")
time.sleep(1)
Example picture for multithreading: Sequential vs Threading
Usually python only has a single program flow, so every instruction needs to complete before the next one can get executed.
For your case you need asynchronicity, with e.g. asyncio.sleep(5) as a separate task in the same event loop.
import asyncio
async def sleeper():
print('Holding...')
await asyncio.sleep(5)
print('Doing Work!')
async def work():
print('Doing work')
print('while')
print('the other guy is sleeping')
async def main():
await asyncio.gather(sleeper(), work())
asyncio.run(main())
The most common and easiest way to implement this would be with a Timer object from the threading library. It would go as follows:
import threading
import time
i = 0
done = False
def show_results():
print("results from GPIO readings")
print("=)")
global done
done = True # signal end of while loop
def read_GPIO():
print("reading GPIO...")
t = threading.Timer(60, show_results) # task will trigger after 60 seconds
t.start()
# your while loop would go here
read_GPIO() # do work
while not done:
print("waiting", i) # doing work while waiting for timer
time.sleep(1)
i += 1
pass
Notice that the time library is used only for illustrative purposes. You could also start the timer recursively to check periodically GPIOs and print results or trigger an event. For more information on the threading library or the Timer object check the docs
Related
I am trying to write a small little GUI that can start an audio recording with one button and end the recording with another.
I have written a recorder class that essentially does the following
class RecordAudio:
def __init__(self):
self.rec = True
def start_recording(self):
while self.rec:
record()
def end_recording(self):
self.rec = False
What is the mechanism, that I can use such that the recording continues on, while still enabling me to stop the recording using the function end_recording()? Or more precisely, what is the best practice for this problem?
I have tried to make the start_recording function async, but this doesn't work, as start_recording never finishes its computation.
Basically I would like to be able to do something like
import asyncio
rec = True
async def start_loop():
global rec
while rec:
await asyncio.sleep(1)
print("Slept another second")
print("Stopped loop")
def stop_loop():
global rec
rec = False
print("Stopping loop")
async def main():
loop = asyncio.get_event_loop()
loop.create_task(start_loop())
await asyncio.sleep(2)
stop_loop()
But where start_loop does not sleep but is continuously performing some endless task.
As said by Michael in the comments, I was using the wrong mode. The documentation gives to modes to operate either a blocking mode or a callback mode. In the callback mode it is possible to start recording in one function, while another function can change the state such that the recording stops.
I need help please, I use schedule.every().day.at("17:40").do(my_function) and I would like my program to run normally and when the schedule.every().day.at("17:40").do(my_function) arrives, it executes the associated function but then it comes back in my loop and wait for another day etc.... I dont know how to do it because i think schedule.every().day.at("17:40").do(my_function) need
while1:
schedule.run_pending()
time.sleep(1)
So i dont know how to changes this 3 lignes to make my programme work.
Thanks!
You would have to run it in separated threading or multiprocessing.
But first you should check documentation because I found in Common Questions:
How to continuously run the scheduler without blocking the main thread?
They created class Scheduler which put it in thread and you need to run run_continuously()
But I use it to created shorter example
import schedule
import time
import threading
# --- functions ---
stop_running = threading.Event() # to control loop in thread
def run_continuously(scheduler, interval=1):
#print('starting loop in thread')
while not stop_running.is_set():
schedule.run_pending()
time.sleep(interval)
#print('stoping loop in thread')
def job():
print("I'm working...")
# --- main ---
schedule.every(1).minutes.do(job)
# run schedule in thread
schedule_in_thread = threading.Thread(target=run_continuously, args=(schedule,))
schedule_in_thread.start()
# run other code
#print('starting main loop')
try:
while True:
print("other code")
time.sleep(3)
except KeyboardInterrupt as ex:
print('stoping', ex)
#print('stoping main loop')
# stop schedule in thread
stop_running.set() # to stop loop in `run_continuously`
schedule_in_thread.join() # wait until thread finish
I use try/except with KeyboardInterrupt only to gracefully stop program when I press Ctrl+C - and code may stop thread.
I have a same thread running every 10 min. but when the new thread starts i want to quit the previous thread so it doesn't keep adding up the space. how can i achieve that. for scheduling of thread.I'm using python schedule library.
this is how I'm scheduling right now
schedule.every(10).minutes.do(sts,threadFunc)
There are two aspects to this question:
identify the currently running job, which is fairly easy.
Kill a running thread in python. There's no great solution for this, and the following code implements the 'stop flag' approach.
I'm solving the first challenge by using a global variable. This variable, named running_thread, holds the currently running thread so that a new job can kill it if needed.
The second challenge requires the running thread to constantly check the status of some flag ('the stop flag'). If the stop flag is set on that thread, it immediately exists.
Here's a code skeleton that demonstrates both these ideas. Jobs take a random amount of time, and I've scheduled them to start every 1 second.
import threading
import time
import schedule
import random
running_thread = None
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_event = threading.Event()
def stop(self):
self._stop_event.set()
def stopped(self):
return self._stop_event.is_set()
def job():
current_thread = threading.currentThread()
sleep_time = random.random() * 5
print(f"Starting job, about to sleep {sleep_time} seconds, thread id is {current_thread.ident}")
counter = 0
while counter < sleep_time:
time.sleep(0.1)
counter += 0.1
if current_thread.stopped():
print ("Stopping job")
break
print(f"job with thread id {current_thread.ident} done")
def threadFunc():
global running_thread
if running_thread:
print("Trying to stop thread")
running_thread.stop()
print("Strting thread")
running_thread = StoppableThread(target = job)
running_thread.start()
schedule.every(1).seconds.do(threadFunc)
while True:
schedule.run_pending()
time.sleep(.5)
This is the code for the progress spinner:
import sys
import time
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
spinner = spinning_cursor()
for _ in range(50):
sys.stdout.write(spinner.next())
sys.stdout.flush()
time.sleep(10)
sys.stdout.write('\b')
Output
python2.7 test.py
|
It is spinning very slowly since the loop sleeps for 10 seconds...
How do I keep rotating the spinner while the process is sleeping?
You'll have to create a separate thread. The example below roughly shows how this can be done. However, this is just a simple example.
import sys
import time
import threading
class SpinnerThread(threading.Thread):
def __init__(self):
super().__init__(target=self._spin)
self._stopevent = threading.Event()
def stop(self):
self._stopevent.set()
def _spin(self):
while not self._stopevent.isSet():
for t in '|/-\\':
sys.stdout.write(t)
sys.stdout.flush()
time.sleep(0.1)
sys.stdout.write('\b')
def long_task():
for i in range(10):
time.sleep(1)
print('Tick {:d}'.format(i))
def main():
task = threading.Thread(target=long_task)
task.start()
spinner_thread = SpinnerThread()
spinner_thread.start()
task.join()
spinner_thread.stop()
if __name__ == '__main__':
main()
You could sleep in smaller steps until you reach 10 seconds:
import sys, time
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
spinner = spinning_cursor()
end_time = time.time() + 10
while time.time() < end_time:
sys.stdout.write(spinner.next())
sys.stdout.flush()
time.sleep(0.2) # adjust this to change the speed
sys.stdout.write('\b')
But this will block your main thread, so it will only be useful if you want to wait for 10 seconds without doing anything else in your Python program (e.g., waiting for some external process to complete).
If you want to run other Python code while the spinner is spinning, you will need two threads -- one for the spinner, one for the main work. You could set that up like this:
import sys, time, threading
def spin_cursor():
while True:
for cursor in '|/-\\':
sys.stdout.write(cursor)
sys.stdout.flush()
time.sleep(0.1) # adjust this to change the speed
sys.stdout.write('\b')
if done:
return
# start the spinner in a separate thread
done = False
spin_thread = threading.Thread(target=spin_cursor)
spin_thread.start()
# do some more work in the main thread, or just sleep:
time.sleep(10)
# tell the spinner to stop, and wait for it to do so;
# this will clear the last cursor before the program moves on
done = True
spin_thread.join()
# continue with other tasks
sys.stdout.write("all done\n")
Spawn two threads, A and B. Thread A runs cmd to completion. Thread B displays the spinning cursor and waits for thread A to exit, which will happen when cmd completes. At that point, thread B clears the spinning cursor and then exit.
Or use an existing library instead of re-inventing the wheel. Consider the progressbar library. You'll want the RotatingMarker progress indicator.
This question already has answers here:
Terminate multiple threads when any thread completes a task
(5 answers)
Closed 8 years ago.
Is it possible to run two threads simultaneously? For example...
I have two classes like this...
import threading
class Thread1(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
print("Thread1")
class justAClass(object):
def do_soemthing(self):
print("Thread2")
if __name__ == "__main__":
total = 0
thread_limit = 200
while True:
if threading.activeCount() < thread_limit:
Thread1().start()
# I will never run because I want to wait until while True has finished to run!
t = threading.Timer(1.0, justAClass().do_soemthing())
t.start()
If you run this code you will see that Tread2 never gets printed out because the Thread2 has to wait for Thread1 to finished (which it never will because of the While statement.
What I'm after is for both Thread1 and Thread2 to run at the same time independent of each other.
while True:
if threading.activeCount() < thread_limit:
Thread1().start()
# I will never run because I want to wait until while True has finished to run!
t = threading.Timer(1.0, justAClass().do_soemthing())
t.start()
obviously that's the case! And as you're never getting out of the loop, the code below the comment is unreachable.
Though, your first code was:
tor.connect()
tor.new_identity()
t = threading.Timer(10.0, tor.new_identity())
t.start()
total = 0
thread_limit = 200
while True:
if threading.activeCount() < thread_limit:
stress_test(host_ip, host_port).start()
And there you were initiating the Timer before the infinite loop, so your Timer thread was definitely working, as we said in our comments. To make your SSCCE work correctly, here comes the fix:
import threading
import time
class Thread1(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
time.sleep(1)
print("Thread1")
class justAClass(object):
def do_something(self, pause):
while True:
time.sleep(pause)
print("Thread2XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
if __name__ == "__main__":
total = 0
thread_limit = 200
t = threading.Timer(1.0, justAClass().do_something, args=(1.0,))
t.start()
while True:
if threading.activeCount() < thread_limit:
Thread1().start()
Though, be aware that your timer thread, with the do_something function as it is, will only run once, unless you rearm it from within the thread, or you build a while loop within.
BTW, I fixed another mistake in your code I did not see at first, you're calling the
timer over the do_something function, be if you pass the do_something function with
parens at the end do_something(), it will be evaluated in your main thread as you're
creating the timer, and then you'll be passing the result of the function to the Timer
function... Whereas if you do not use the parens, you're giving the function object itself
to Timer() which will then be able to call it after the delay.
is there a way to get the timer to run every x seconds while still allowing the other function to run?
of course:
class justAClass(object):
def do_something(self, pause):
while True:
time.sleep(pause)
print("Thread2XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")