C# 定时器在间隔结束前触发

问题描述 投票:0回答:1

我在 C# 中使用 System.Timers.Timer 来安排需要每 5 秒执行一次的任务。但是,我注意到计时器有时会在间隔结束之前触发。这是我的代码:

internal class IcbDataProcessor : BackgroundService
{
    private readonly ILogger<IcbDataProcessor> _logger;
    private readonly System.Timers.Timer _timer;
    private readonly TimeSpan _interval = TimeSpan.FromSeconds(5);

    public IcbDataProcessor(ILogger<IcbDataProcessor> logger)
    {
        _logger = logger;
        _timer = new System.Timers.Timer(_interval);
        _timer.Elapsed += Timer_Elapsed;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var now = DateTime.Now;
        var nextTime = RoundUp(now, _interval);
        if (nextTime > now)
        {
            await Task.Delay(nextTime - now, stoppingToken);
        }
        _timer.Start();
    }

    private static DateTime? _prevRoundedTime;
    private void Timer_Elapsed(object? sender, ElapsedEventArgs e)
    {
        try
        {
            var roundedTime = RoundDown(DateTime.Now, _interval);
            if (roundedTime == _prevRoundedTime)
            {
                _logger.LogWarning("IcbDataProcessor: Prev Rounded Time is equals to Rounded Time: {Time}", roundedTime);
            }

            //To do
            _prevRoundedTime = roundedTime;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "DataProcessor EXCEPTION {Message}", ex.Message);
        }
    }

    public override async Task StopAsync(CancellationToken cancellationToken)
    {
        _timer?.Dispose();
        await Task.CompletedTask;
    }

    public static DateTime RoundUp(DateTime date, TimeSpan d)
    {
        return new DateTime((date.Ticks + d.Ticks - 1) / d.Ticks * d.Ticks, date.Kind);
    }

    public static DateTime RoundDown(DateTime date, TimeSpan d)
    {
        return new DateTime(date.Ticks / d.Ticks * d.Ticks, date.Kind);
    }
}

当我检查存储的时间时,我发现计时器启动时存在如下情况:

10:10:25.001
10:10:29.999
。显然,上一个实例和下一个实例之间的间隔还不到 5 秒,导致我的
roundedTime
被四舍五入到相同的数字。我已经检查并确信我的事件完成得非常快(大约 20 毫秒)并且我的代码中没有错误。

有谁知道为什么会发生这种情况以及如何解决它?任何建议表示赞赏。谢谢!

c# system.timers.timer
1个回答
0
投票

假设您像代码中一样使用

DateTime.Now
来检查时间,让我们看一个时间流逝的示例,如下所示:

活动 间隔 已过去
Timer_Elapsed
被解雇了
0
经过一些 CPU 周期后
获取
DateTime.Now
我₀ 0+i₀
Timer_Elapsed
被解雇了
5.01
经过一些 CPU 周期后
获取
DateTime.Now
我₁ 5.01+i₁

两次检查之间的间隔是

5.01 - (i₀ - i₁)
。如果在事件触发过程中
i
的值突然增加,将会导致你计算的间隔出现偏差。

解决方案取决于您想要做什么。

  1. 如果您只想每 N 秒执行一个方法,那么您假设计时器始终按照设定的时间间隔正确运行。

  2. 如果想在一定时间内执行某个方法N次,可以记录执行次数。如果在指定时间后执行次数不够,您可以额外添加一次(就像闰秒一样)。

© www.soinside.com 2019 - 2024. All rights reserved.