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

System.Timers.Timer失步:如何精准触发方法同步视频触觉反馈

解决定时器延迟堆叠问题的优化方案

问题根源

你使用的System.Timers.Timer在AutoReset=true模式下,会在Elapsed事件完全执行完毕后才重新开始计时。如果streamSensation和calcManagerTick的总耗时超过100ms,下一次触发就会被推迟,且延迟会不断累积,这就是运行15秒后滞后2秒的核心原因。

优化方案一:手动重置Timer+时间修正

把Timer改为手动重置模式,结合Stopwatch计算实际需要的等待间隔,抵消延迟堆叠:

private int tick = 0;
private System.Timers.Timer timer;
private System.Diagnostics.Stopwatch watch;
private readonly long targetIntervalMs = 100;
// 存储上一次计算的触觉数据,避免计算超时无数据可发
private Sensation _currentSensation;

private YourConstructor() {
    timer = new System.Timers.Timer(targetIntervalMs);
    timer.Elapsed += OnTimerElapsed;
    timer.AutoReset = false; // 关闭自动重置,改为手动控制
    timer.Enabled = false;
    // 初始化默认触觉数据
    _currentSensation = new Sensation();
}

private void OnTimerElapsed(Object source, ElapsedEventArgs e) {
    // 1. 优先发送上一次计算好的数据
    streamSensation(_currentSensation);
    tick++;

    // 2. 计算下一次的触觉数据
    var newSensation = calcManagerTick(tick);
    if (newSensation != null) {
        _currentSensation = newSensation;
    }

    // 3. 修正下一次触发间隔,抵消延迟
    long expectedMs = tick * targetIntervalMs;
    long actualMs = watch.ElapsedMilliseconds;
    // 计算需要等待的时间,若为负则立即触发(用1ms避免0值异常)
    long nextInterval = Math.Max(1, expectedMs - actualMs);
    
    timer.Interval = nextInterval;
    timer.Start();
}

private void streamSensation(Sensation sensation) {
    // 发送触觉数据逻辑
    Console.WriteLine($"{tick} / {watch.ElapsedMilliseconds}");
}

private Sensation calcManagerTick(int currentTick) {
    // 根据当前tick加载并计算触觉数据的逻辑
    return new Sensation();
}

private void start() {
    if (!timer.Enabled) {
        watch = Stopwatch.StartNew();
        // 提前计算第一个tick的数据
        _currentSensation = calcManagerTick(tick);
        timer.Start();
    }
}

方案优势

  • 改动量小,符合你原有的Timer使用习惯
  • 通过时间修正逻辑,自动抵消单次计算带来的延迟,避免堆叠
  • 确保计算超时后,发送的是上一次的有效数据,满足需求3

优化方案二:异步Task循环(更灵活)

用Task.Run结合Task.Delay实现异步定时循环,计算逻辑不阻塞定时流程,彻底避免延迟堆叠:

private int tick = 0;
private System.Diagnostics.Stopwatch watch;
private readonly long targetIntervalMs = 100;
private Sensation _currentSensation;
private CancellationTokenSource _cts;
private readonly object _dataLock = new object();

private YourConstructor() {
    _currentSensation = new Sensation();
}

private async void start() {
    if (_cts != null && !_cts.IsCancellationRequested) return;

    _cts = new CancellationTokenSource();
    watch = Stopwatch.StartNew();
    tick = 0;

    while (!_cts.IsCancellationRequested) {
        // 1. 发送当前存储的触觉数据(优先保证发送时机)
        lock (_dataLock) {
            streamSensation(_currentSensation);
        }
        tick++;

        // 2. 异步计算下一次数据,不阻塞定时流程
        _ = Task.Run(() => {
            var newSensation = calcManagerTick(tick);
            lock (_dataLock) {
                _currentSensation = newSensation;
            }
        }, _cts.Token);

        // 3. 计算下一次等待时间,修正延迟
        long expectedMs = tick * targetIntervalMs;
        long actualMs = watch.ElapsedMilliseconds;
        long waitMs = expectedMs - actualMs;

        // 若计算耗时超过间隔,立即进入下一轮
        if (waitMs > 0) {
            await Task.Delay(TimeSpan.FromMilliseconds(waitMs), _cts.Token);
        }
    }
}

private void streamSensation(Sensation sensation) {
    // 发送触觉数据逻辑
    Console.WriteLine($"{tick} / {watch.ElapsedMilliseconds}");
}

private Sensation calcManagerTick(int currentTick) {
    // 根据当前tick加载并计算触觉数据的逻辑
    return new Sensation();
}

private void stop() {
    _cts?.Cancel();
    _cts?.Dispose();
    _cts = null;
}

方案优势

  • 计算逻辑异步执行,完全不影响定时发送的节奏
  • 天然支持计算超时场景,自动重复发送上一次数据
  • 后续若要和视频播放时间同步,可直接在循环中获取视频当前播放时间修正tick,实现更精准的同步

额外同步建议

如果要和视频定时器更精准同步,不要完全依赖本地Stopwatch,可以定期获取视频的当前播放时间(比如每2-3秒一次),然后修正本地tick值:

// 假设视频播放控件提供GetCurrentPlaybackTimeMs()方法
long videoCurrentMs = videoControl.GetCurrentPlaybackTimeMs();
tick = (int)(videoCurrentMs / targetIntervalMs);

这样可以抵消本地时间和视频时间的漂移,确保触觉触发和视频内容对齐。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 03:07:07