How to efficiently do many tasks a “little later” in Python?

后端 未结 10 929
心在旅途
心在旅途 2021-01-30 11:59

I have a process, that needs to perform a bunch of actions \"later\" (after 10-60 seconds usually). The problem is that those \"later\" actions can be a lot (1000s), so using a

10条回答
  •  攒了一身酷
    2021-01-30 12:29

    This answer has actually two suggestions - my first one and another I have discovered after the first one.

    sched

    I suspect you are looking for the sched module.

    EDIT: my bare suggestion seemed little helpful after I have read it. So I decided to test the sched module to see if it can work as I suggested. Here comes my test: I would use it with a sole thread, more or less this way:

    class SchedulingThread(threading.Thread):
    
        def __init__(self):
            threading.Thread.__init__(self)
            self.scheduler = sched.scheduler(time.time, time.sleep)
            self.queue = []
            self.queue_lock = threading.Lock()
            self.scheduler.enter(1, 1, self._schedule_in_scheduler, ())
    
        def run(self):
            self.scheduler.run()
    
        def schedule(self, function, delay):
            with self.queue_lock:
                self.queue.append((delay, 1, function, ()))
    
        def _schedule_in_scheduler(self):
            with self.queue_lock:
                for event in self.queue:
                    self.scheduler.enter(*event)
                    print "Registerd event", event
                self.queue = []
            self.scheduler.enter(1, 1, self._schedule_in_scheduler, ())
    

    First, I'd create a thread class which would have its own scheduler and a queue. At least one event would be registered in the scheduler: one for invoking a method for scheduling events from the queue.

    class SchedulingThread(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.scheduler = sched.scheduler(time.time, time.sleep)
            self.queue = []
            self.queue_lock = threading.Lock()
            self.scheduler.enter(1, 1, self._schedule_in_scheduler, ())
    

    The method for scheduling events from the queue would lock the queue, schedule each event, empty the queue and schedule itself again, for looking for new events some time in the future. Note that the period for looking for new events is short (one second), you may change it:

        def _schedule_in_scheduler(self):
            with self.queue_lock:
                for event in self.queue:
                    self.scheduler.enter(*event)
                    print "Registerd event", event
                self.queue = []
            self.scheduler.enter(1, 1, self._schedule_in_scheduler, ())
    

    The class should also have a method for scheduling user events. Naturally, this method should lock the queue while updating it:

        def schedule(self, function, delay):
            with self.queue_lock:
                self.queue.append((delay, 1, function, ()))
    

    Finally, the class should invoke the scheduler main method:

        def run(self):
            self.scheduler.run()
    

    Here comes an example of using:

    def print_time():
        print "scheduled:", time.time()
    
    
    if __name__ == "__main__":
        st = SchedulingThread()
        st.start()          
        st.schedule(print_time, 10)
    
        while True:
            print "main thread:", time.time()
            time.sleep(5)
    
        st.join()
    

    Its output in my machine is:

    $ python schedthread.py
    main thread: 1311089765.77
    Registerd event (10, 1, , ())
    main thread: 1311089770.77
    main thread: 1311089775.77
    scheduled: 1311089776.77
    main thread: 1311089780.77
    main thread: 1311089785.77
    

    This code is just a quick'n'dirty example, it may need some work. However, I have to confess that I am a bit fascinated by the sched module, so did I suggest it. You may want to look for other suggestions as well :)

    APScheduler

    Looking in Google for solutions like the one I've post, I found this amazing APScheduler module. It is so practical and useful that I bet it is your solution. My previous example would be way simpler with this module:

    from apscheduler.scheduler import Scheduler
    import time
    
    sch = Scheduler()
    sch.start()
    
    @sch.interval_schedule(seconds=10)
    
    def print_time():
        print "scheduled:", time.time()
        sch.unschedule_func(print_time)
    
    while True:
        print "main thread:", time.time()
        time.sleep(5)
    

    (Unfortunately I did not find how to schedule an event to execute only once, so the function event should unschedule itself. I bet it can be solved with some decorator.)

提交回复
热议问题