You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python threading.Timer设700ms发TCP请求 耗时短于设定值原因排查

固定延时触发请求间隔小于设定值问题排查与修复

问题现象

  • 设定700ms延时触发第二帧拍照并向TCP服务端发送数据,服务端统计到的请求间隔集中在682ms~699ms区间,小于设定的700ms阈值
  • 实拍秒表读取的两帧时间差和服务端统计值完全一致,排除网络传输、多端时间戳不同步导致的统计偏差
  • 按预期逻辑,700ms延时叠加代码执行、网络传输耗时,总间隔应大于700ms,实际结果不符合预期

核心根因

问题出在两个常见的定时逻辑开发误区:

  1. 原生定时API不保证精度
    threading.Event.wait()、threading.Timer、time.sleep()这类原生等待接口,底层依赖操作系统的线程调度机制,本身不提供硬实时保证:
  • Windows平台默认全局定时器分辨率为15.6ms,定时等待的天然误差范围在±15ms区间,系统负载高、线程优先级低的时候偏差会更大
  • 这类接口仅做软实时保证,当系统定时器分辨率被其他程序修改、系统时钟被NTP同步调整时,会出现提前返回的情况
  1. 延时起点计算错误
    从代码执行流程看,主观认为的延时起点是第一次拍照完成的时刻,但实际不管是Timer的start()调用,还是子线程里wait()的计时起点,都和预期起点存在偏差:
  • 如果用threading.Timer实现,调用start()的那一刻就已经开始计时,之后执行第一次拍照、线程初始化的耗时都会直接从总延时里扣除,如果你在第一次拍照前就启动了Timer,那15ms左右的代码执行耗时刚好对应观测到的间隔差值
  • 如果用Event.wait实现,子线程从创建到被CPU调度执行、调用wait方法的过程存在调度延迟,叠加wait本身的误差,最终实际触发时间就会偏离设定值

修复方案

不要依赖单次等待接口的超时参数做精确延时,改用单调时钟+绝对时间校准的方案,具体实现如下:

  1. 所有时间间隔计算统一用time.perf_counter(),这个接口是系统单调时钟,不受NTP时间同步、系统时钟回拨影响,精度可达微秒级
  2. 在第一次拍照完成的时刻立刻记录时间戳,计算好第二次触发的绝对目标时间
  3. 用短间隔循环等待的方式替代单次长等待,每次最多等待1ms就校准一次剩余时间,直到当前时间达到目标触发点,再执行后续拍照、发送逻辑

修复后的核心代码:

import time
import threading

def snapshot(self):
    # 执行第一次拍照
    ImageProcessing.getInstance().serviceSnapshot(camera, DataToQeue)
    # 记录第一次拍照完成的单调时间戳
    first_finish_tick = time.perf_counter()
    target_delay = 0.7  # 目标延时700ms
    event_obj = threading.Event()
    # 把起始时间、目标延时作为参数传入子线程
    threading.Thread(
        target=generate2ndTicketBasedonTime,
        args=[self.attributes, self.car.object_id, event_obj, first_finish_tick, target_delay]
    ).start()

def generate2ndTicketBasedonTime(attributes, object_id, event_obj, start_tick, delay):
    target_trigger_tick = start_tick + delay
    while True:
        current_tick = time.perf_counter()
        remain_wait = target_trigger_tick - current_tick
        if remain_wait <= 0:
            break
        # 每次最多等1ms,避免长wait的精度误差
        event_obj.wait(min(remain_wait, 0.001))
    # 到达目标时间,触发第二次拍照和发送逻辑
    ImageProcessing.getInstance().serviceSnapshot(camera, DataToQeue)
    event_obj.set()

额外优化项

  • 如果部署在Windows平台,可以调用Windows Multimedia API的timeBeginPeriod(1)把系统全局定时器分辨率调整到1ms,进一步降低线程调度带来的误差,注意该操作会小幅增加系统功耗
  • 网络发送逻辑中,建议把triggerTime的记录移到sendall调用之前,同样用time.perf_counter()记录,避免send阻塞带来的时间统计偏差

修改后重新测试,帧间隔和服务端统计的请求间隔误差可以控制在±2ms以内,不会再出现间隔小于设定值的问题。

内容的提问来源于stack exchange,提问作者Hazem Hatem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 11:27:22