Python timer start and reset - python

I am attempting to get a Timer functionality to work in Python (Python 2.7 currently).
Here is what I have so far. I am struggling with a threading issue and resetting the timer.
from threading import Timer
def api_call():
print("Call that there api")
t = Timer(10.0,api_call)
def my_callback(channel):
if something_true:
print('reset timer and start again')
t.cancel()
t.start()
print("\n timer started")
elif something_else_true:
t.cancel()
print("timer canceled")
else:
t.cancel()
print('cancel timer for sure')
try:
if outside_input_that_can_happen_a_lot:
my_callback()
finally:
#cleanup objects
Basically, my_callback() can be called a lot of times very quickly and can hit any part of the "if", "elif", or "else" statements.
The issue I am having is that when the something_true variable is true, then it will start a timer. Which works great the first time. Every time after that that it is called, I get a threading error telling me that only one thread can be used for the timer.
Basically, I want to be able to reset my timer on the first "if" and cancel if the "elif" or "else" is hit.

Based on my testing, this is because threads can only be started once, and as the timer relies on a thread, the timer can only be started once.
This means that the only way to re-start the timer would be to do:
def newTimer():
global t
t = Timer(10.0,api_call)
newTimer()
instead of the t = Timer part, and do
t.cancel()
newTimer()
t.start()
instead of the current re-start code.
This makes your full code:
from threading import Timer
def api_call():
print("Call that there api")
def newTimer():
global t
t = Timer(10.0,api_call)
newTimer()
def my_callback(channel):
if something_true:
print('reset timer and start again')
t.cancel()
newTimer()
t.start()
print("\n timer started")
elif something_else_true:
t.cancel()
print("timer canceled")
else:
t.cancel()
print('cancel timer for sure')
try:
if outside_input_that_can_happen_a_lot:
my_callback()
finally:
#cleanup objects
Hope this helps.

Related

How to program a task with a timer in my Python code?

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

i want to understand echedule.every().day.at how can it be in paralle with another thread

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.

Run a function in a seperate python thread

(Python 3.8.3)
I am using two python threads right now, one that has a while True loop
import threading
def threadOne():
while True:
do(thing)
print('loop ended!')
t1=threading.Thread(threadOne)
t1.start()
And another that checks for a ctrl+r input. When recieved, I need the second thread to tell the first thread to break from the while loop. Is there a way to do this?
Note that I cannot change the loop to 'while Break == False' as do(thing) waits for user input, but i need this to be interrupted.
The recommended way is to use threading.event (You can combine this with event.wait if you want to sleep in that thread too however as you are waiting for a user event, probably dont need that).
import threading
e = threading.Event()
def thread_one():
while True:
if e.is_set():
break
print("do something")
print('loop ended!')
t1=threading.Thread(target=thread_one)
t1.start()
# and in other thread:
import time
time.sleep(0.0001) # just to show thread_one keeps printing
# do something for little bit and then it break
e.set()
EDIT: To interrupt the thread while it's waiting for user input you can send SIGINT to that thread and and it will raise KeyboardInterrupt which you can then handle. Unfortunate limitation of python, including python3, is that signals to all threads are handled in the main thread so you need to wait for the user input in the main thread:
import threading
import sys
import os
import signal
import time
def thread_one():
time.sleep(10)
os.kill(os.getpid(), signal.SIGINT)
t1=threading.Thread(target=thread_one)
t1.start()
while True:
try:
print("waiting: ")
sys.stdin.readline()
except KeyboardInterrupt:
break
print("loop ended")

python schedule library to stop previously running thread when new scheduled thread starts

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)

non-block thread with kill function in python

It is general in programming in Python that when I have a function or something which when I call, it blocks my code to proceed. So I think the best way to unblock is using threads but If I need to stop a thread what should I do?
I tried this reference and I wrote this simple program:
import threading
from time import sleep
class my_thread(threading.Thread):
"""Thread class with a stop() method. The thread itself has to check
regularly for the stopped() condition."""
def __init__(self):
super(my_thread, self).__init__()
self._stop_event = threading.Event()
def stop(self):
print("stopping the thread")
self._stop_event.set()
def stopped(self):
value=self._stop_event.is_set()
print("value of stop event is",value)
return value
def run(self):
print("running the thread")
print("start function startt()")
self.startt()
def startt(self):
print("it is going to wait forever")
while True:
#wait forever
pass
print("This line never execute")
def main():
for i in range(0,3):
print("it is the main function")
sleep(1)
if __name__+'__main__':
thr=my_thread()
thr.start()
sleep(5)
thr.stop()
thr.stopped()
print("calling the main function")
main()
print("Exiting the whole program")
My problem is this program actually stop the thread but after printing the last line the program still runs. What I want is if I call the stop function thr.start() it starts the thread and run #wait forever line and if I call the stop function thr.stop() it stop the whole class and returns from #wait forever line to the main function.
EDIT--
As #a_guest answer I can fix it but my problem is general for example If I had this code instead of while True:
pythoncom.PumpMessages()
(or any other code)
what should I do?
Instead of
while True:
...
you should use
while not self.stopped():
...
Then it will break out of the while loop once you stop() the thread.
You can't "abort" a running thread so to stop it you'll have to have a mechanism in the thread itself that periodically checks if it should stop.
Regular threads keep running while the rest of your program (process) exits.
If you make your thread a 'daemon' thread however, it will get killed automatically when your program exits. To do that, set self.daemon=True in your thread's init method. More info https://docs.python.org/3/library/threading.html#threading.Thread.daemon

Categories