Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#计时器在每四舍五入的小时内滴答作响(字面意思)_C# - Fatal编程技术网

C#计时器在每四舍五入的小时内滴答作响(字面意思)

C#计时器在每四舍五入的小时内滴答作响(字面意思),c#,C#,我的任务是每小时从API中获取一些信息。我所说的每小时是指每小时,如:10:00、11:00等。以下代码在每小时执行DoWork方法,但不是在每一个整小时(如10:00)执行,而是在服务启动后1小时执行 public class SelfHostedService : IHostedService, IDisposable { private readonly ILogger _logger; private Timer _timer; public SelfHoste

我的任务是每小时从API中获取一些信息。我所说的每小时是指每小时,如:10:00、11:00等。以下代码在每小时执行
DoWork
方法,但不是在每一个整小时(如10:00)执行,而是在服务启动后1小时执行

public class SelfHostedService : IHostedService, IDisposable
{
    private readonly ILogger _logger;
    private Timer _timer;

    public SelfHostedService(ILogger<SelfHostedService> logger)
    {
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Timed Background Service is starting.");

        _timer = new Timer(DoWork, null, TimeSpan.Zero,
            TimeSpan.FromHours(1));

        return Task.CompletedTask;
    }

    private void DoWork(object state)
    {
        _logger.LogInformation("Timed Background Service is working.");
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Timed Background Service is stopping.");

        _timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}
公共类自托管服务:IHostedService,IDisposable
{
专用只读ILogger\u记录器;
私人定时器;
公共自宿服务(ILogger记录器)
{
_记录器=记录器;
}
公共任务StartSync(CancellationToken CancellationToken)
{
_logger.LogInformation(“定时后台服务正在启动”);
_计时器=新计时器(DoWork、null、TimeSpan.Zero、,
时间跨度从小时(1)开始;
返回Task.CompletedTask;
}
私有无效工作(对象状态)
{
_logger.LogInformation(“定时后台服务正在工作。”);
}
公共任务StopAsync(CancellationToken CancellationToken)
{
_logger.LogInformation(“定时后台服务正在停止”);
_计时器?更改(Timeout.Infinite,0);
返回Task.CompletedTask;
}
公共空间处置()
{
_计时器?.Dispose();
}
}
编辑:

public class BotHostedService : IHostedService
{
    private readonly ILogger _logger;
    private CancellationTokenSource _tokenSource;
    private Task _timer;

    public BotHostedService(ILogger<BotHostedService> logger)
    {
        _logger = logger;
    }

    private DateTime RoundCurrentToNextOneHour()
    {
        DateTime now = DateTime.Now, result = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0);
        return result.AddMinutes(((now.Minute / 60) + 1) * 60);
    }

    private async Task RunPeriodically(Action action, DateTime startTime, TimeSpan interval, CancellationToken token)
    {
        DateTime nextRunTime = startTime;

        try
        {
            while (true)
            {
                TimeSpan delay = nextRunTime - DateTime.Now;

                if (delay > TimeSpan.Zero)
                {
                    await Task.Delay(delay, token);
                }

                action();

                nextRunTime += interval;
            }
        }
        catch (OperationCanceledException)
        {
            return;
        }
    }

    private void DoWork()
    {
        _logger.LogInformation($"Timed Background Service is working. Current time: {DateTime.Now.ToLocalTime()}");
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Timed Background Service is starting.");

        if (_timer == null)
        {
            _tokenSource = new CancellationTokenSource();
            DateTime startTime = RoundCurrentToNextOneHour();
            _timer = RunPeriodically(DoWork, startTime, TimeSpan.FromHours(1), _tokenSource.Token);
        }

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Timed Background Service is stopping.");

        if (_timer != null)
        {
            _timer = null;

            _tokenSource.Cancel();
            _tokenSource.Dispose();
            _tokenSource = null;
        }

        return Task.CompletedTask;
    }
}
公共类BotHostedService:IHostedService
{
专用只读ILogger\u记录器;
私有取消令牌源\u令牌源;
专用任务计时器;
公共BotHostedService(ILogger记录器)
{
_记录器=记录器;
}
专用日期时间RoundCurrentToNextOneHour()
{
DateTime now=DateTime.now,result=newdatetime(now.Year,now.Month,now.Day,now.Hour,0,0);
返回结果.AddMinutes(((now.Minute/60)+1)*60);
}
私有异步任务定期运行(操作操作、日期时间开始时间、时间间隔、取消令牌)
{
DateTime nextRunTime=startTime;
尝试
{
while(true)
{
TimeSpan delay=nextrutime-DateTime.Now;
如果(延迟>时间跨度为零)
{
等待任务。延迟(延迟,令牌);
}
动作();
下一个时间+=间隔;
}
}
捕获(操作取消异常)
{
返回;
}
}
私房
{
_logger.LogInformation($”定时后台服务正在工作。当前时间:{DateTime.Now.ToLocalTime()}”);
}
公共任务StartSync(CancellationToken CancellationToken)
{
_logger.LogInformation(“定时后台服务正在启动”);
如果(_timer==null)
{
_tokenSource=新的CancellationTokenSource();
DateTime startTime=RoundCurrentToNextOneHour();
_计时器=定期运行(DoWork、startTime、TimeSpan.FromHours(1),\u tokenSource.Token);
}
返回Task.CompletedTask;
}
公共任务StopAsync(CancellationToken CancellationToken)
{
_logger.LogInformation(“定时后台服务正在停止”);
如果(_timer!=null)
{
_定时器=空;
_tokenSource.Cancel();
_tokenSource.Dispose();
_tokenSource=null;
}
返回Task.CompletedTask;
}
}

制作另一个时间间隔为1分钟的计时器,检查分钟值,然后当分钟数为0时,启动主计时器,然后关闭辅助计时器

因此,计算距离下一个小时的时间,并为该时间设置计时器。例如,查看
DateTime.Now.Minute
。值得在每次滴答声时重新进行计算,而不是将其精确设置为1小时——一个1小时的计时器会随着时间慢慢漂移(尽管相对漂移量非常小)用整小时的时间来初始化计时器,并在第一次工作时将其更改为精确的1小时。请参阅标记的副本上的我的答案。特别是,第二个示例使用了
RoundCurrentToNextFiveMinutes()
。只需修改该方法,使其四舍五入到下一个小时,而不是下一个五分钟(当然,重命名该方法,使其名称仍然正确指示其功能)。谢谢!我编辑了我的答案。转换为
TimeSpan
时是否准确?就目前而言,它看起来还可以。但请记住,我对另一个问题的回答也确保了计时与时钟同步,因此“每五分钟”或“每小时”实际上将继续精确(或尽可能接近)地发生在实际的五分钟/小时标记上。内置计时器本质上是不准确的,并且会随着时间的推移而漂移,因此您可能会发现,经过一段时间(可能是几个小时,可能是几天),您的计时器不再在一个小时的最高峰启动。