C#计时器在每四舍五入的小时内滴答作响(字面意思)
我的任务是每小时从API中获取一些信息。我所说的每小时是指每小时,如:10:00、11:00等。以下代码在每小时执行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
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
时是否准确?就目前而言,它看起来还可以。但请记住,我对另一个问题的回答也确保了计时与时钟同步,因此“每五分钟”或“每小时”实际上将继续精确(或尽可能接近)地发生在实际的五分钟/小时标记上。内置计时器本质上是不准确的,并且会随着时间的推移而漂移,因此您可能会发现,经过一段时间(可能是几个小时,可能是几天),您的计时器不再在一个小时的最高峰启动。