Python定时器深度解析:从threading.Timer缺陷到高精度PyTimer实现

蓝鲸
蓝鲸
发布时间:2026-10-16| 6人浏览过
原创

本文深入分析Python标准库中threading.Timer在连续定时任务中的误差累计缺陷,并介绍一种自定义PyTimer类。该实现支持单次与连续定时,采用多线程非阻塞执行,通过动态调整休眠时间将误差控制在10毫秒级别且不累计,满足高精度定时需求。

Python定时器现状与基础实现

定时器是一种按固定间隔执行特定任务的机制,几乎每种编程语言都提供了相应的实现。例如,Java提供了util.Timer和util.TimerTask,JavaScript拥有setInterval和setTimeout,它们都能处理复杂的定时逻辑。然而,功能强大的Python却缺乏一个直观且完善的内置定时器,这显得有些出人意料。

初学者可能会提到time.sleep函数,认为设置好时间后等待即可,这看似具备定时器的基本特征。但实际上,time.sleep存在两个主要局限:首先,它会阻塞主线程,导致在等待期间无法执行其他操作;其次,即使借助线程技术,也需要主线程先创建子线程来执行任务,增加了复杂性。

熟悉threading模块的开发者可能会指出threading.Timer,它通过线程运行,不阻塞主线程且无需主线程干预即可执行任务,看似是一个理想的解决方案。

让我们先了解threading.Timer的工作机制。以下代码演示了其基本用法:启动一个定时器,2秒后以线程方式调用do_something函数。在等待的2秒内以及do_something执行期间,主线程仍可执行其他操作——此处通过键盘输入阻塞主线程,以便观察定时器的行为。


import time
import threading

def do_something(name, gender='male'):
    print(time.time(), '定时时间到,执行特定任务' )
    print('name:%s, gender:%s'%(name, gender))

timer = threading.Timer(2, do_something, args=('Alice',), kwargs={'gender':'female'})
timer.start()
print(time.time(), '定时开始时间')
input('按回车键结束\n') # 此处阻塞住进程

如预期所示,定时器在启动2秒后调用do_something函数,期间可随时按回车键结束程序。运行结果如下。

1627438957.4297626 定时开始时间

按回车键结束

1627438959.4299397 定时时间到,执行特定任务

name:Alice, gender:female

threading.Timer的误差累计问题

从使用效果来看,threading.Timer简洁易用。但其明显短板在于不支持连续的定时任务,例如每隔2秒调用一次do_something函数。若强行使用threading.Timer实现连续定时,只能采用嵌套方式,即在do_something函数内部再次启动定时器。


import time
import threading

def do_something(name, gender='male'):
    global timer
    timer = threading.Timer(2, do_something, args=(name,), kwargs={'gender':gender})
    timer.start()
    
    print(time.time(), '定时时间到,执行特定任务' )
    print('name:%s, gender:%s'%(name, gender))
    time.sleep(5)
    print(time.time(), '完成特定任务' )

timer = threading.Timer(2, do_something, args=('Alice',), kwargs={'gender':'female'})
timer.start()
input('按回车键结束\n') # 此处阻塞住进程

上述代码重新定义了do_something函数,在函数起始处启动下一次定时任务。将其置于起始位置是为了尽量保证两次定时之间的时间间隔精确。然而,运行结果显示,两次定时之间的间隔比设定的2秒多了约10毫秒,且误差会连续累计。重复执行100次后,误差可能超过1秒。

按回车键结束

1627440628.683803 定时时间到,执行特定任务

name:Alice, gender:female

1627440630.6929214 定时时间到,执行特定任务

name:Alice, gender:female

1627440632.707388 定时时间到,执行特定任务

name:Alice, gender:female

1627440633.6890671 完成特定任务

1627440634.722474 定时时间到,执行特定任务

name:Alice, gender:female

1627440635.7092102 完成特定任务

1627440636.7277966 定时时间到,执行特定任务

name:Alice, gender:female

理想定时器的设计标准

对于连续定时任务,threading.Timer的表现尚可,但这种嵌套写法严重破坏了代码的美学。对于追求代码整洁的开发者而言,这种实现方式难以接受。一个理想的定时器应满足以下5个条件,并具备相应的结构。

  1. 不阻塞主线程
  2. 同时支持单次定时和连续定时
  3. 以线程或进程方式执行定时任务
  4. 定时任务的线程或进程的创建、运行,不影响定时精度
  5. 足够精确的定时精度,且误差不会累计

高精度PyTimer的实现与测试

既然Python未提供完善的定时器,我们可以自行实现。以下定时器满足上述5个条件,最短时间间隔可低至10毫秒,且误差不会累计。虽然并非完美,但在结构和精度上已相当不错。


import time
import threading

class PyTimer:
    """定时器类"""
    
    def __init__(self, func, *args, **kwargs):
        """构造函数"""
        
        self.func = func
        self.args = args
        self.kwargs = kwargs
        self.running = False
    
    def _run_func(self):
        """运行定时事件函数"""
        
        th = threading.Thread(target=self.func, args=self.args, kwargs=self.kwargs)
        th.setDaemon(True)
        th.start()
    
    def _start(self, interval, once):
        """启动定时器的线程函数"""
        
        if interval < 0.010:
            interval = 0.010
        
        if interval < 0.050:
            dt = interval/10
        else:
            dt = 0.005
        
        if once:
            deadline = time.time() + interval
            while time.time() < deadline:
                time.sleep(dt)
            
            # 定时时间到,调用定时事件函数
            self._run_func()
        else:
            self.running = True
            deadline = time.time() + interval
            while self.running:
                while time.time() < deadline:
                    time.sleep(dt)
                
                # 更新下一次定时时间
                deadline += interval
                
                # 定时时间到,调用定时事件函数
                if self.running:
                    self._run_func()
    
    def start(self, interval, once=False):
        """启动定时器
        
        interval    - 定时间隔,浮点型,以秒为单位,最高精度10毫秒
        once        - 是否仅启动一次,默认是连续的
        """
        
        th = threading.Thread(target=self._start, args=(interval, once))
        th.setDaemon(True)
        th.start()
    
    def stop(self):
        """停止定时器"""
        
        self.running = False

实例化PyTimer类时需传入定时任务函数。若任务函数有参数,可按位置参数和关键字参数的顺序提供。PyTimer提供start和stop两个方法,分别用于启动和停止定时器。stop方法无需参数,start方法需传入以秒为单位的定时间隔参数。start还有一个布尔型默认参数once,用于设置是否单次定时。once默认为False,即默认连续定时;若需单次定时,只需将once设为true。


def do_something(name, gender='male'):
    print(time.time(), '定时时间到,执行特定任务' )
    print('name:%s, gender:%s'%(name, gender))
    time.sleep(5)
    print(time.time(), '完成特定任务' )

timer = PyTimer(do_something, 'Alice', gender='female')
timer.start(0.5, once=False)

input('按回车键结束\n') # 此处阻塞住进程
timer.stop()

以上示例展示了使用PyTimer以0.5秒间隔连续调用do_something函数的情况。运行结果如下。

按回车键结束

1627450313.425347 定时时间到,执行特定任务

name:Alice, gender:female

1627450313.9226055 定时时间到,执行特定任务

name:Alice, gender:female

1627450314.421761 定时时间到,执行特定任务

name:Alice, gender:female

1627450314.9243422 定时时间到,执行特定任务

name:Alice, gender:female

1627450315.422722 定时时间到,执行特定任务

name:Alice, gender:female

1627450315.9200313 定时时间到,执行特定任务

name:Alice, gender:female

1627450316.4204514 定时时间到,执行特定任务

name:Alice, gender:female

1627450316.9215539 定时时间到,执行特定任务

name:Alice, gender:female

1627450317.4228196 定时时间到,执行特定任务

name:Alice, gender:female

1627450317.9245899 定时时间到,执行特定任务

name:Alice, gender:female

1627450318.42355 定时时间到,执行特定任务

name:Alice, gender:female

1627450318.4393418 完成特定任务

1627450318.9251466 定时时间到,执行特定任务

name:Alice, gender:female

1627450318.9395308 完成特定任务

1627450319.4242043 完成特定任务

1627450319.4242043 定时时间到,执行特定任务

name:Alice, gender:female

1627450319.9253905 定时时间到,执行特定任务

name:Alice, gender:female

1627450319.9411068 完成特定任务

1627450320.425871 完成特定任务

1627450320.425871 定时时间到,执行特定任务

name:Alice, gender:female

尽管每个定时任务需运行5秒,但每隔0.5秒都会准时启动新线程执行任务。从记录可见,尽管每次启动有数毫秒误差,但误差不会累计,重复执行的时间间隔均值始终稳定在0.5秒。

至此,关于基于Python实现简单定时器的详解已完成。如需了解更多Python定时器相关内容,请查阅本站往期文章或继续浏览后续内容,感谢支持!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。