在调用scheduler.enter
后使用scheduler.run
调度事件时,在触发运行调度程序之前输入的至少一个事件之前,不会触发这些事件。这是为了吗?
import sched
import threading
import time
now = time.time()
scheduler = sched.scheduler(time.time, time.sleep)
def fire(name):
print('EVENT:', name, time.time() - now)
def start():
t = threading.Thread(target=scheduler.run)
t.start()
###################################
# when scheduling all before start() everything works as expected.
# scheduler.enter(4, 1, fire, ('D',))
# scheduler.enter(3, 1, fire, ('C',))
# scheduler.enter(2, 1, fire, ('B',))
# scheduler.enter(1, 1, fire, ('A',))
# start()
#
# EVENT: A 1.0
# EVENT: B 2.0
# EVENT: C 3.0
# EVENT: D 4.0
###################################
# here I'd expect event 'A' to fire after one second. But it is
# not fired until at least one of the events scheduled before run()
# is fired.
# scheduler.enter(2, 1, fire, ('B',))
# scheduler.enter(4, 1, fire, ('D',))
# start()
# scheduler.enter(1, 1, fire, ('A',))
# scheduler.enter(3, 1, fire, ('C',))
#
# EVENT: A 2.005021572113037
# EVENT: B 2.005021572113037
# EVENT: C 3.0050315856933594
# EVENT: D 4.0050413608551025
###################################
# starting before any event is scheduled is not possible,
# because run() returns directly.
#
# start()
# scheduler.enter(3, 1, fire, ('C',))
# scheduler.enter(2, 1, fire, ('B',))
# scheduler.enter(1, 1, fire, ('A',))
#
# -- no events fired.
答案 0 :(得分:3)
启动调度程序时,它会查找到下一个事件的延迟,并使用该值调用delayfunc(time.sleep()
)。在delayfunc返回之前,调度程序无法触发任何其他事件。
我对sched
模块没有任何解决方法。它的设计似乎是基于这样的想法:新事件将从现有事件的处理程序中添加,而不是来自一个单独的线程(事实上,它甚至不支持在多线程环境中使用,直到Python 3.3 )。使用新的async
内容可能是更好的方法,但我没有任何特别的建议。
答案 1 :(得分:0)
请记住,如果您可以将任务分解为定期重复的固定工作,也可以使用schedule
。
但是在这种情况下,我的解决方案是如图所示子类化调度程序,并使用廉价的私有函数进行轮询。这使调度程序成为永久性的,并且(至少)以轮询间隔的粒度“监听”更新。我设置了一个可配置的轮询速率,默认为每秒4次-非常适合GUI反馈。在生产环境中,正在运行的线程应为守护程序,以免干扰终止请求。这是您代码的修改版本。准备使用时,从民意调查中删除print('tick')
。
import sched
import threading
import time
now = time.time()
class scheduler_with_polling(sched.scheduler):
def __init__(self, timefn, waitfn, **kwargs):
super().__init__(timefn, waitfn)
self.polling_interval = kwargs.get('polling_interval', 0.25)
def run(self):
self.enter(self.polling_interval, 1, self.__poll)
super().run()
def __poll(self):
print('tick') # remove for production
self.enter(self.polling_interval, 1, self.__poll)
pass
scheduler = scheduler_with_polling(time.time, time.sleep, polling_interval=0.2)
def fire(name):
print('EVENT:', name, time.time() - now)
def start():
t = threading.Thread(target=scheduler.run) # use daemon=True for production
t.start()
scheduler.enter(2, 1, fire, ('B',))
scheduler.enter(4, 1, fire, ('D',))
start()
scheduler.enter(1, 1, fire, ('A',))
scheduler.enter(3, 1, fire, ('C',))