线程休眠/等待直到新的一天

14

我正在循环运行一个进程,它每天有一定数量的操作限制。当它达到这个限制时,我目前让它在循环中检查时间,看是否到了新的一天。

那么最好的选择是:

  • 每秒钟检查一次时间,以判断是否到了新的一天
  • 计算距离午夜还有多少秒,然后等待相应的时间
  • 其他什么方法?

1
使用计时器也是一种解决方案! - Arnaud F.
重复问题:http://stackoverflow.com/questions/752326/best-way-to-schedule-tasks-in-c - Olivier
我认为有些不同,因为我并不是为每天的特定时间安排日程,而是等待动态的时间量,可能会有稍微更好的解决方案。 - finoutlook
5个回答

17
不要使用Thread.Sleep来实现这种操作。使用计时器并计算需要等待的时间。
var now = DateTime.Now;
var tomorrow = now.AddDays(1);
var durationUntilMidnight = tomorrow.Date - now;

var t = new Timer(o=>{/* Do work*/}, null, TimeSpan.Zero, durationUntilMidnight);

用指定的回调函数替换 /* Do Work */ 委托,以在指定间隔内恢复工作。

编辑:如评论中所述,如果您假设应用程序等待的“经过时间”将与实际时间相匹配,可能会出现许多问题。因此,如果时间很重要,最好使用较小的轮询间隔来查找时钟是否已达到您希望进行工作的时间。

更好的方法是使用Windows任务计划程序在所需时间运行任务。这比尝试在代码中自己实现要可靠得多。


2
我建议将计时器间隔设置为比午夜前的毫秒数更频繁的时间。 (如果有时间更改怎么办?等等。如果您按照UTC并且时钟从未更改,则可能不是问题)。 - James Johnston
应用程序对时间变化过于严格并不重要,如果时间发生变化,程序将在新的午夜版本重新启动。 - finoutlook

7

Windows有一个任务计划程序,可以处理这个任务。创建要完成的程序,然后将其设置为定期任务。


那么您的意思是在一天内进行处理,然后结束,等待任务计划程序再次启动?虽然这可能是一个不错的解决方案,但我认为该应用程序应该是始终运行的东西(最终成为一个 Windows 服务)。 - finoutlook
@finoutlook - 成为服务不仅仅是长时间运行。如果需要成为服务,那就把它变成一个服务。添加计时器并不能帮助你以后进行转换。 - P.Brian.Mackey

3

只需计算等待的时间并以异步定时器的方式运行,这样您就可以避免在等待期间额外消耗CPU:

var nextDateStartDateTime = DateTime.Now.AddDays(1).Subtract(DateTime.Now.TimeOfDay);
double millisecondsToWait = (nextDateStartDateTime - DateTime.Now).TotalMilliseconds;

System.Threading.Timer timer = new Timer(
    (o) => { Debug.WriteLine("New day comming on"); },
    null,
    (uint)millisecondsToWait
    0);

谢谢,这非常接近我要实现的解决方案(将答案给丹·赫伯特,因为他首先提到了计时器)。 - finoutlook

0

考虑您提供的两个选项:

每天有60 * 60 * 24 = 86,400秒,因此如果您早期达到限制,您可能会进行大量检查。此外,忙等待是浪费CPU周期的行为,它会减慢运行的所有其他内容。

您应该计算到午夜的秒数并睡眠那么长时间(尽管我认为睡眠参数需要毫秒而不是秒,因此可能需要进行简单的转换)。

编辑:

计算然后睡眠的另一个好处是,如果用户想通过更改时钟来绕过您的限制,他们将无法做到这一点(因为时钟读取午夜不会像持续检查一样唤醒进程)。但是,如果更好地了解您的程序在内部如何工作,用户可以在即将达到操作限制的时候将时钟更改为接近午夜,从而使线程在几分钟甚至几秒钟内唤醒。这比您的第一个建议更复杂的利用,但是它是可行的。


谢谢,是的,我想避免在等待时进行处理。 - finoutlook
是的 - 频繁唤醒会消耗电量,Windows 将不得不将他的应用程序部分交换到 RAM 中一直保持运行。然而,如果时钟更改,则在进行大量初始计算的情况下睡眠到午夜可能不准确。可能需要做出一些妥协... - James Johnston
不需要非常准确,而且这是一个内部应用程序,所以没有最终消费者会干扰时间 :) - finoutlook

-3

这是我如何使线程睡眠直到明天早上6点

minutesToSleep = (int)(new DateTime(DateTime.Now.AddDays(1).Year, DateTime.Now.AddDays(1).Month, DateTime.Now.AddDays(1).Day, 6, 0, 0) - DateTime.Now).TotalMinutes;
Console.WriteLine("Sleeping for {0} minutes (until tomorrow 6AM)", minutesToSleep);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接