15.12. sched — 时间事件调度器

未匹配的标注

目的:通用事件调度器。

sched 模块实现了一个通用事件调度器,用于在特定时间运行任务。 调度器类使用 time 函数来获知当前时间,并使用 delay 函数来等待特定的时间段。 实际的时间单位并不重要,这使得接口足够灵活,可用于多种用途。

在没有任何参数的情况下调用 time 函数,并且应返回表示当前时间的数字。 delay 函数使用单个整数参数调用,使用与时间函数相同的比例,并且应该在返回之前等待那么多时间单位。 默认情况下,使用 monotonic()sleep() 来自 time ,但是本节中的示例使用 time.time() ,它也符合要求,因为它使输出更容易理解。

为了支持多线程应用程序,在生成每个事件后使用参数 0 调用 delay 函数,以确保其他线程也有机会运行。

延迟运行事件

事件可以安排在一段延迟之后或在特定时间运行。 要使用延迟计划它们,请使用 enter() 方法,该方法需要四个参数。

  • 代表延迟的数字
  • 优先级
  • 被调用的函数
  • 函数的参数元组

此示例分别计划了两个不同的事件,分别在两秒和三秒后运行。 当事件的时间到来时,调用 print_event() 并打印当前时间和传递给事件的 name 参数。

sched_basic.py

import sched
import time

scheduler = sched.scheduler(time.time, time.sleep)

def print_event(name, start):
    now = time.time()
    elapsed = int(now - start)
    print('EVENT: {} elapsed={} name={}'.format(
        time.ctime(now), elapsed, name))

start = time.time()
print('START:', time.ctime(start))
scheduler.enter(2, 1, print_event, ('first', start))
scheduler.enter(3, 1, print_event, ('second', start))

scheduler.run()

运行程序会产生:

$ python3 sched_basic.py

START: Sun Sep  4 16:21:01 2016
EVENT: Sun Sep  4 16:21:03 2016 elapsed=2 name=first
EVENT: Sun Sep  4 16:21:04 2016 elapsed=3 name=second

第一个事件打印的时间是启动后两秒,第二个事件的时间是启动后三秒。

重叠事件

直到所有事件都被处理完毕前,对 run() 的调用将阻塞。 每个事件都在同一个线程中运行,因此如果事件运行的时间比事件之间的延迟长,则会产生重叠。 重叠会通过推迟后一事件来解决。 没有事件会丢失,但某些事件可能比计划的时间晚。 在下一个例子中, long_event() 会休眠但是通过执行长计算或阻塞 I/O 可以很容易地延迟。

sched_overlap.py

import sched
import time

scheduler = sched.scheduler(time.time, time.sleep)

def long_event(name):
    print('BEGIN EVENT :', time.ctime(time.time()), name)
    time.sleep(2)
    print('FINISH EVENT:', time.ctime(time.time()), name)

print('START:', time.ctime(time.time()))
scheduler.enter(2, 1, long_event, ('first',))
scheduler.enter(3, 1, long_event, ('second',))

scheduler.run()

结果是第二个事件在第一个事件结束后立即运行,因为第一个事件消耗了足够长的时间来使当前时间超过第二个事件的期望开始时间。

$ python3 sched_overlap.py

START: Sun Sep  4 16:21:04 2016
BEGIN EVENT : Sun Sep  4 16:21:06 2016 first
FINISH EVENT: Sun Sep  4 16:21:08 2016 first
BEGIN EVENT : Sun Sep  4 16:21:08 2016 second
FINISH EVENT: Sun Sep  4 16:21:10 2016 second

事件优先级

如果同时安排了多个事件,则使用其优先级值来确定它们的运行顺序。

sched_priority.py

import sched
import time

scheduler = sched.scheduler(time.time, time.sleep)

def print_event(name):
    print('EVENT:', time.ctime(time.time()), name)

now = time.time()
print('START:', time.ctime(now))
scheduler.enterabs(now + 2, 2, print_event, ('first',))
scheduler.enterabs(now + 2, 1, print_event, ('second',))

scheduler.run()

这个例子需要确保它们被安排在同一时间,因此使用 enterabs() 方法而不是 enter()enterabs() 的第一个参数是运行事件的时间,而不是延迟的时间。

$ python3 sched_priority.py

START: Sun Sep  4 16:21:10 2016
EVENT: Sun Sep  4 16:21:12 2016 second
EVENT: Sun Sep  4 16:21:12 2016 first

取消事件

enter()enterabs() 都返回对事件的引用,可以在以后取消它。 因为 run() 阻塞运行,所以必须在不同的线程中取消该事件。 对于此示例,启动线程以运行调度,主器处理线程用于取消事件。

sched_cancel.py

import sched
import threading
import time

scheduler = sched.scheduler(time.time, time.sleep)

# 设置要由线程修改的全局变量
counter = 0

def increment_counter(name):
    global counter
    print('EVENT:', time.ctime(time.time()), name)
    counter += 1
    print('NOW:', counter)

print('START:', time.ctime(time.time()))
e1 = scheduler.enter(2, 1, increment_counter, ('E1',))
e2 = scheduler.enter(3, 1, increment_counter, ('E2',))

# 启动一个线程来运行事件
t = threading.Thread(target=scheduler.run)
t.start()

# 回到主线程,取消第一个预定的事件。
scheduler.cancel(e1)

# 等待调度器在线程中完成运行
t.join()

print('FINAL:', counter)

安排了两个事件,但第一个事件后来取消了。 仅运行第二个事件,因此计数器变量仅增加了一次。

$ python3 sched_cancel.py

START: Sun Sep  4 16:21:13 2016
EVENT: Sun Sep  4 16:21:16 2016 E2
NOW: 1
FINAL: 1

另请参阅

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/pymotw/sched-ti...

译文地址:https://learnku.com/docs/pymotw/sched-ti...

上一篇 下一篇
贡献者:1
讨论数量: 0
发起讨论 只看当前版本


暂无话题~