我正在开发一个必须在特定完全时间执行方法的Windows服务。
当服务启动时,它会从SQL Server数据库中提取几个不同的DateTime,然后应为每个DateTime启动一个线程。每个线程应该等到确切的DateTime值,然后做一些工作,然后再等待一段较短的时间,然后再做一些最后的工作。
我不想使用System.Timers.Timer,因为:
我该如何实现?
谢谢。
编辑:昨晚我刚才有了一个想法,如果我写一个动态轮询计时器怎么办?例如,如果到期日期时间超过12小时,则可以每小时轮询时间以重新同步。然后,如果到期日期时间超过3小时,它可以每隔30分钟轮询一次,依此类推......你怎么看?这会是一个糟糕的代码吗?
再次感谢你。
答案 0 :(得分:2)
您无法使用System.Timers.Timer
将参数传递给tick事件,但您可以使用System.Threading.Timer
。延迟时间大于24小时"没问题。定时器使用32位周期,以毫秒为单位。 2 ^ 31毫秒可以达到24.85天。还有一个重载,允许您使用64位有符号整数指定延迟。 2 ^ 63毫秒是......几亿年。
但让这些计时器在未来的确切日期/时间打勾是有问题的,因为你必须指定延迟时间,时钟更改会导致你的延迟太短或太长(想想夏令时,或用户发起的时间变化)。您可以指定计时时间并让计时器每秒轮询当前时间。它有点难看,但它确实有效。
另一种选择是使用Windows Waitable Timer对象,它允许您设置确切的日期和时间。不幸的是,框架并没有包含相应的对象。几年前我写了一篇关于它的文章并发布了代码。该文章已不再在线提供,但您可以从http://mischel.com/pubs/waitabletimer.zip下载代码。
答案 1 :(得分:1)
我过去使用了一些名为 ManualResetEvent 的东西并取得了很大的成功。假设您知道运行代码的确切时间,那么您将能够计算估计的运行时间(TTR),并且应该在第一次运行中配置后续运行。
partial class SomeService : ServiceBase
{
private ManualResetEvent stop = new ManualResetEvent(false);
private List<DateTime> times;
private int idxDT = 0;
public SomeService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
this.stop.Reset();
//implement you logic to calculate miliseconds to desired first run time
int miliseconds_to_run = 1;
ThreadPool.RegisterWaitForSingleObject(this.stop,
new WaitOrTimerCallback(ThreadFunc),
null,
miliseconds_to_run,
true);
}
private void ThreadFunc(object _state, bool _timedOut)
{
if (_timedOut)
{
if(this.times == null)
{
//get a list of times to run, store it along with the index of current TTR
this.times = new List<DateTime>();
}
int miliseconds_to_run = (this.times[this.idxDT++] - DateTime.Now).Miliseconds;
ThreadPool.RegisterWaitForSingleObject(this.stop,
new WaitOrTimerCallback(ThreadFunc),
null,
miliseconds_to_run,
true);
}
}
protected override void OnStop()
{
this.stop.Set();
}
}
当然,这在很大程度上取决于您的工作开始时间的准确程度。 ThreadPool 类将向操作系统发送一个线程调配请求,然后它将等待来自池的下一个可用线程。在一些拥有大量线程的进程中,这可能导致线程饥饿,并且您的确切时间将会延迟。
您也可以尝试从.NET创建任务计划程序作业,但我以前从未这样做过。
答案 2 :(得分:1)
即使你说你不想使用System.Timers.Timer
我也会告诉你如何去做,因为你问题中的子弹1和2似乎不是在我看来反对使用它的有效点。
所以这就是我在我的几个应用程序中所做的事情:
// 1. Create the timer
System.Timers.Timer timer = new System.Timers.Timer();
timer.AutoReset = false;
timer.Elapsed += ...;
// 2. Calculate the number of milliseconds between the scheduled time and the current time
timer.Interval = (scheduledDate - DateTime.Now).TotalMilliSeconds;
// 3. Start the timer
timer.Start();
完成。该活动将大概开始#34;在期望的时间。请注意,由于Windows不是实时操作系统,因此无法精确到毫秒级。
答案 3 :(得分:1)
我确实面临类似问题,并使用下面的解决方案来解决您的问题。
我使用了ManualResetEvent类,添加了一个Wait方法,并使服务等待了确切的时间。
protected ManualResetEvent waitEvent = new ManualResetEvent(false);
protected bool Wait(int milliSecs)
{
return !this.waitEvent.WaitOne(milliSecs, true);
}
我在OnStart方法中使用了Wait,如下所示:
protected override void OnStart(string[] args)
{
DateTime nextRun = dt;//datetime from the database
YourProcessOrThreadToRun process = new YourProcessOrThreadToRun();
while (Wait((int)nextRun.Subtract(DateTime.Now).TotalMilliseconds))
{
process.StartProcess();
}
}
YourProcessOrThreadToRun是您希望在该确切时间运行的线程。