Python, schedule parallel Threads with one thread for each method - python

How I can run those two tasks in parallel, but if the Thread with the name of the method was not finished yet just skip this method till the next schedule iteration?
Because now it creates a new thread for the same method while it is running.
def task1:
#do task1
def task1:
#do task2
def run_threaded(job_fn):
job_thread = threading.Thread(target=job_fn)
job_thread.start()
schedule.every(5).minutes.do(run_threaded, task1)
schedule.every(3).minutes.do(run_threaded, task2)
while True:
schedule.run_pending()
time.sleep(1)

Figured out with another module called apscheduler.
It has parameter max_instances:1 and log thing like this
*Execution of job "task1 (trigger: interval[0:50:0], next run at: 2019-02-16 11:38:23 EET)" skipped: maximum number of running instances reached (1)*
scheduler = BackgroundScheduler(executors=executors, job_defaults=job_defaults)
scheduler.add_job(task1, 'interval', minutes=5)
scheduler.add_job(task2, 'interval', minutes=7)
scheduler.start()
You don't need to create a threading.Thread because module doing this for you. Just pass your method.

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

Sequentially run pending tasks with Python APS

Suppose I have two cron triggers:
trigger1 = CronTrigger(second='0,20,40')
trigger2 = CronTrigger(second='0,10,20,30,40,50')
and I create my scheduler like this:
scheduler = BlockingScheduler()
scheduler.add_job(lambda: method1(param1, param2), trigger=trigger1)
scheduler.add_job(lambda: method2(param1, param3), trigger=trigger2)
with these two methods which do work:
def method1(s, t):
print("doing work in method1")
time.sleep(2)
print("doing work in method1")
time.sleep(2)
print("doing work in method1")
time.sleep(2)
def method2(s, t):
print("doing work in method2")
time.sleep(2)
print("doing work in method2")
time.sleep(2)
print("doing work in method2")
time.sleep(2)
When the scheduled times overlap(eg 0, 20, 30) and the scheduler has two jobs scheduled for that time, it seems to run them in parellel. The output looks like this:
doing work in method1
doing work in method2
doing work in method1
doing work in method2
doing work in method1
doing work in method2
Question is: How do I set it up so that the pending jobs are run sequentially. ie. if the times of two jobs overlap, run the first job until completion, then run the second one.
Edit: The reason I have used the apsschedule library is because I need cron-like functionality. I need the process to run between certain times of the day at certain intervals.
Use DebugExecutor.
For example:
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.executors.debug import DebugExecutor
def foo1():
print("x")
def foo2():
time.sleep(3)
print("y")
scheduler = BlockingScheduler()
scheduler.add_executor(DebugExecutor(), "consecutive")
scheduler.add_job(foo1, 'interval', max_instances=1, seconds=1, executor="consecutive")
scheduler.add_job(foo2, 'interval', max_instances=1, seconds=5, executor="consecutive")
Use DebugExecutor is a good idea, additionally I needed to specify a high value for the misfire_grace_time parameter in .add_job() to avoid skipping runs when multiple jobs have same execution interval

Schdeuling jobs after calling .start() APscheduler

I am writing a program to schedule and cancel alarms in Flask. I am using the apscheduler library for the timings.
I need to be able to add events to the job queue at any point, so I need to be able to add events after the scheduler is run.
Currently, I have:
from apscheduler.schedulers.background import BackgroundScheduler
def cancel():
job = events[0]
job.remove()
def schedule():
sched = scheds[0]
try:
sched.shutdown()
except:
pass
job = sched.add_job(my_job, 'date', run_date=t, args=['text'])
events.append(job)
sched.start()
def schedule2():
sched = scheds[0]
try:
sched.shutdown()
except:
pass
job = sched.add_job(my_job, 'date', run_date=t2, args=['text'])
events.append(job)
sched.start()
Where scheds is an array to store a global scheduler, and events is an array which stores the events that are scheduled.
I need to run schedule, then schedule2, to schedule two different jobs. When I try this, I get an error which says that I cannot run schedule2 because the 'scheduler is already running'. How can I achieve this?

How schedule a job (Django, Python)

I would like to create a job that rolls to all 10 munites.
I find a good example here. The problem is that the program is freezing during the waiting time and my other urls are blocked.
after me it's because of while True:
Is there a way to do it without going around this problem?
voici le code:
import schedule
import time
def job():
print("I'm working...")
schedule.every(10).minutes.do(job)
while True:
schedule.run_pending()
time.sleep(1)
*******************************************************************.
I found the right way to do it. Here is the link:
For that to work well, I removed this part:
# time.sleep(20)
# print('Checkpoint **************************')
# time.sleep(30)
# print('Bye -----------------------')
Here is the code that works:
import threading
class ThreadingExample(object):
""" Threading example class
The run() method will be started and it will run in the background
until the application exits.
"""
def __init__(self, interval=10):
""" Constructor
:type interval: int
:param interval: Check interval, in seconds
"""
self.interval = interval
thread = threading.Thread(target=self.run, args=())
thread.daemon = True # Daemonize thread
thread.start() # Start the execution
def run(self):
""" Method that runs forever """
while True:
# Do something
print('Doing something imporant in the background', self.interval)
pk_info_semaine = job_temp.objects.all()
for a in pk_info_semaine:
print('num_semaine:',a.num_semaine,'user_id:',a.user_id)
time.sleep(self.interval)
example = ThreadingExample()
Thank you all and thank you to the author: Paris Nakita Kejser Here
You can use celery + celerybeat together with Django to run scheduled tasks. You can write your method as a celery task, and add an entry in your settings.py file to make the task run every 10 minutes. The task will run in its on thread, hence not blocking your application.
voici le link to celery:
http://docs.celeryproject.org/en/latest/django/first-steps-with-django.html

Python - Apscheduler not stopping a job even after using 'remove_job'

This is my code
I'm using the remove_job and the shutdown functions of the scheduler to stop a job, but it keeps on executing.
What is the correct way to stop a job from executing any further?
from apscheduler.schedulers.background import BlockingScheduler
def job_function():
print "job executing"
scheduler = BlockingScheduler(standalone=True)
scheduler.add_job(job_function, 'interval', seconds=1, id='my_job_id')
scheduler.start()
scheduler.remove_job('my_job_id')
scheduler.shutdown()
Simply ask the scheduler to remove the job inside the job_function using the remove_function as #Akshay Pratap Singh Pointed out correctly, that the control never returns back to start()
from apscheduler.schedulers.background import BlockingScheduler
count = 0
def job_function():
print "job executing"
global count, scheduler
# Execute the job till the count of 5
count = count + 1
if count == 5:
scheduler.remove_job('my_job_id')
scheduler = BlockingScheduler()
scheduler.add_job(job_function, 'interval', seconds=1, id='my_job_id')
scheduler.start()
As you are using BlockingScheduler , so first you know it's nature.
So, basically BlockingScheduler is a scheduler which runs in foreground(i.e start() will block the program).In laymen terms, It runs in the foreground, so when you call start(), the call never returns. That's why all lines which are followed by start() are never called, due to which your scheduler never stopped.
BlockingScheduler can be useful if you want to use APScheduler as a standalone scheduler (e.g. to build a daemon).
Solution
If you want to stop your scheduler after running some code, then you should opt for other types of scheduler listed in ApScheduler docs.
I recommend BackgroundScheduler, if you want the scheduler to run in the background inside your application/program which you can pause, resume and remove at anytime, when you need it.
The scheduler needs to be stopped from another thread. The thread in which scheduler.start() is called gets blocked by the scheduler. The lines that you've written after scheduler.start() is unreachable code.
This is how I solved the problem. Pay attention to the position where the code schedule.shutdown() is located!
def do_something():
global schedule
print("schedule execute")
# schedule.remove_job(id='rebate')
schedule.shutdown(wait=False)
if __name__ == '__main__':
global schedule
schedule = BlockingScheduler()
schedule.add_job(do_something, 'cron', id='rebate', month=12, day=5, hour=17, minute=47, second=35)
schedule.start()
print('over')

Categories