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

C#有限循环中运行Timer如何实现精准计时 解决跳秒问题

问题描述
  • 业务需求:实现摄像头视频分段录制能力,用户启动摄像头录制后,每录制5秒自动停止并保存为独立文件,随后自动开启下一段5秒时长的录制,流程按用户预设的次数(示例为3次)循环执行。
  • 现存问题:现有逻辑演示代码可基本运行,但偶发秒数跳变异常,比如界面显示1秒后卡顿2秒,随后直接跳显2、3秒等数值。视频录制对时长精度要求极高,现有实现存在计时偏差导致输出错误的风险。
原有实现代码
Stopwatch sw;
private void btnStart_Click(object sender, EventArgs e)
{
    sw = new Stopwatch();
    timer1.Enabled = true;
    sw.Start();
}
int counter = 1;
private void timer1_Tick(object sender, EventArgs e)
{
    if (sw.Elapsed > TimeSpan.FromSeconds(4))
    {
        sw.Reset();
       
        if (counter < 3)
        {
            sw.Start();
            counter++;
        }
        else
        {
            sw.Reset();
            timer1.Stop();
            counter = 0;
            MessageBox.Show("Counter has finished +"counter.ToString());
        }
    }
    lblTime.Text = string.Format("{0:hh\\:mm\\:ss}", sw.Elapsed);
    
}
问题根因
  • 现有代码使用的System.Windows.Forms.Timer是绑定UI线程消息队列的组件,消息优先级极低,只要UI线程被其他操作占用(比如界面重绘、控件响应、文件IO),Tick事件就会被延迟甚至丢失触发,这就是秒数跳变的核心原因,这类Timer天生不适合高精度计时场景。
  • 现有逻辑分段阈值设为4秒,和需求要求的5秒分段本身就存在设计偏差。
  • 每次分段都重置、重启Stopwatch的逻辑,会把Stopwatch启停、录制开关、文件保存的耗时逐段累积,循环次数越多总时长偏差越大。
  • 计时判断和UI刷新逻辑完全耦合,UI卡顿时计时逻辑会直接被阻塞。
优化方案
  • 替换计时组件:抛弃WinForm默认的UI Timer,改用System.Timers.Timer(精度约15ms)或者Windows多媒体计时器(精度可达1ms),计时逻辑运行在独立线程,不受UI线程阻塞影响,更新UI时通过控件的Invoke方法跨线程调度即可。
  • 修正时间判断逻辑:不要反复重置重启Stopwatch,录制启动时启动全局Stopwatch,每一段录制开始时记录当前段的起始时间戳,每次计时回调时直接计算当前时间和段起始时间的差值,达到5秒立刻触发分段保存逻辑,从根源避免累积误差。
  • 逻辑解耦:把UI刷新频率设为100ms/次即可满足人眼查看需求,分段触发逻辑完全独立于UI刷新,不受UI卡顿影响。
  • 异步处理IO操作:分段时的视频保存、录制重启操作放到独立线程执行,不要阻塞计时线程。
优化后参考代码
private Stopwatch sw;
private System.Timers.Timer highPrecisionTimer;
private int segmentCount = 0;
private readonly int totalSegments = 3;
private readonly TimeSpan segmentLength = TimeSpan.FromSeconds(5);
private long currentSegmentStartTimestamp;

private void btnStart_Click(object sender, EventArgs e)
{
    segmentCount = 0;
    sw = Stopwatch.StartNew();
    currentSegmentStartTimestamp = sw.ElapsedTicks;
    
    // 初始化高精度计时器,间隔10ms检查一次分段条件
    highPrecisionTimer = new System.Timers.Timer(10);
    highPrecisionTimer.Elapsed += HighPrecisionTimer_Elapsed;
    highPrecisionTimer.AutoReset = true;
    highPrecisionTimer.Start();
    
    // 此处添加启动第一段录制的业务逻辑
}

private void HighPrecisionTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    long currentTicks = sw.ElapsedTicks;
    TimeSpan currentSegmentElapsed = new TimeSpan(currentTicks - currentSegmentStartTimestamp);
    
    // 检查是否达到分段时长
    if (currentSegmentElapsed >= segmentLength)
    {
        segmentCount++;
        // 异步执行停止录制、保存文件、启动下一段逻辑,避免阻塞计时线程
        Task.Run(() =>
        {
            // 此处添加停止当前录制、保存文件的业务逻辑
            if (segmentCount < totalSegments)
            {
                // 此处添加启动下一段录制的业务逻辑
                currentSegmentStartTimestamp = sw.ElapsedTicks;
            }
            else
            {
                highPrecisionTimer.Stop();
                sw.Stop();
                // 跨线程调度UI提示
                lblTime.Invoke((Action)(() =>
                {
                    MessageBox.Show($"Counter has finished {segmentCount}");
                }));
            }
        });
    }
    
    // 跨线程更新UI时间显示
    lblTime.Invoke((Action)(() =>
    {
        lblTime.Text = currentSegmentElapsed.ToString(@"hh\:mm\:ss");
    }));
}

如果对录制精度要求到10ms以内,可以把System.Timers.Timer替换为Windows多媒体计时器(timeSetEvent API),精度可达1ms级别,完全满足专业视频录制的时长要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 03:45:47