Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Usage of builtin sched module's non-blocking scheduler.run() method?

I'd like to understand how to use the optional parameter blocking in the method scheduler.run(blocking=True). Any practical/real-world example would be very helpful.

Based on the research I've done so far, the intention of the blocking optional argument is for non-blocking or async applications[1][2]. Below is the main run loop of the schduler (from the python 3.6 library sched.py). Following through the code, I notice that whenever blocking is set to False, immediately returns the time difference between target time and current time, unless the target time had passed, in which case the action would be executed.

while True:
    with lock:
        if not q:
            break
        time, priority, action, argument, kwargs = q[0]
        now = timefunc()
        if time > now:
            delay = True
        else:
            delay = False
            pop(q)
    if delay:
        if not blocking:
            return time - now
        delayfunc(time - now)
    else:
        action(*argument, **kwargs)
        delayfunc(0)   # Let other threads run

Seems to me the non-blocking design requires me to keep running the scheduler until the queue is clean. Thus, I'm thinking about maintaining a task queue myself and keep pushing the scheduler.run task into the queue (like the code below.) Is this a desirable design? What is the proper way of using the non-blocking scheduler?

def action():
    print('action at: ', datetime.now())

if __name__ == '__main__':
    s = sched.scheduler(time.time)
    target_time = datetime.now() + timedelta(seconds=5)
    s.enterabs(target_time.timestamp(), 1, action)
    run = functools.partial(s.run, blocking=False)
    taskq = deque()
    taskq.append(run)
    while taskq:
        task = taskq.popleft()
        result = task()
        print(result)
        if result:
            taskq.append(run)
            time.sleep(1)

    print('end tasks')

[1] What’s New In Python 3.3

[2] Issue13449: sched - provide an "async" argument for run() method

like image 565
dawranliou Avatar asked Jan 20 '17 00:01

dawranliou


People also ask

What is sched module in Python?

The sched module defines a class which implements a general purpose event scheduler: class sched. scheduler(timefunc=time.monotonic, delayfunc=time.sleep) The scheduler class defines a generic interface to scheduling events.

How does Python sched work?

Schedule lets you run Python functions (or any other callable) periodically at pre-determined intervals using a simple, human-friendly syntax. Schedule Library is used to schedule a task at a particular time every day or a particular day of a week. We can also set time in 24 hours format that when a task should run.

How do I run a scheduler in Python?

To run your python scheduler you will need to create a task, create an action, add the path to your python executable file and to your python script and add a trigger to schedule your script.

What is the name of pythons built in module for general purpose event scheduler?

Sched module is the standard library, can be used in the creation of bots and other monitoring and automation applications. The sched module implements a generic event scheduler for running tasks at specific times.


1 Answers

Old question, but I just implemented something which used the nonblocking version pretty effectively.

When blocking = True in sched.scheduler.run, it will call the delayfunc for the time difference until the next event.

This may be undesirable if your application, at t = 0, schedules an event A for t = 10, but another thread, at t = 1, schedules an event B for t = 5. In this case,

s = sched.scheduler(time.time)
# Spawn threads which enter A and B into s
while True:
    s.run(True)

if your main thread is just calling sched.scheduler.run(blocking=True) in a loop, at t = 0 it will call delayfunc(10) because it only sees that it has 10 time units left until A. The main thread won't wake up until t = 10, at which point it will see that it missed B, run B 5 time units late, and then run A after B.

To solve this, you can change the main thread to look like this:

s = sched.scheduler(time.time)
# Spawn threads which enter A and B into s
while True:
    next_ev = s.run(False)
    if next_ev is not None:
        time.sleep(min(1, next_ev))
    else:
        time.sleep(1)

This code will catch up on all present events, then sleep until the next event, or if there is no next event or if the next event is too far ahead, will sleep for 1 second.

Ideally, scheduler would be implemented with a condition variable for if a new event reaches the front of the priority queue, and it could wait on that variable instead of just sleeping until the next event. This would be the most efficient and most time accurate.

like image 190
bathtub Avatar answered Oct 24 '22 23:10

bathtub