如何以毫秒精度在特定时间触发C#函数?

我有2台计算机的时间通过NTP同步,这确保时间只有几毫秒的差异。 其中一台计算机将通过TCP向另一台计算机发送消息,以便在将来的两台计算机上的指定时间启动某个c#function。

我的问题是:如何在特定时间以毫秒精度(或更好)触发C#中的函数? 我需要在程序代码中执行此操作(因此任务计划程序或其他外部程序将无济于事)。 总是在一个单独的线程中循环来比较当前时间和目标时间不是一个很好的解决方案我猜。

更新:

DateTime.Now不能在解决方案中使用,因为它具有低分辨率。

似乎Thread.Sleep()可以通过导入强制具有1 ms的分辨率:

[DllImport("winmm.dll", EntryPoint="timeBeginPeriod")] public static extern uint MM_BeginPeriod(uint uMilliseconds); 

和使用:

 MM_BeginPeriod(1); 

要恢复到先前的分辨率导入:

 [DllImport("winmm.dll", EntryPoint = "timeEndPeriod")] public static extern uint MM_EndPeriod(uint uMilliseconds); 

并使用:

 MM_EndPeriod(1); 

更新2:

我用很多值测试了Thread.Sleep(),看起来它作为一个平均值会趋向于指定的时间跨度。

仅调用Thread.Sleep()一次通常会在目标值时间范围内保持大约半毫秒左右,因此对于毫秒级分辨率来说非常精确。

使用winmm.dll方法timeBeginPeriod和timeEndPeriod似乎对结果的准确性没有影响。

解:

一种方法是使用timeSetEvent(不建议使用)或CreateTimerQueueTimer。

目前的问题是,两者都需要作为一个参数,剩余的时间直到函数触发而不是它应该触发的时间。 因此必须计算触发器所需时间之前的延迟,但DateTime.Now提供低分辨率。 我找到了一个允许高分辨率获取当前DateTime的类。 所以现在剩下的时间可以用高分辨率计算,并作为参数传递给CreateTimerQueueTimer。

这应该每毫秒给你一个事件。 您可以使用秒表来测量经过的时间。 使用调用在主UI线程上触发事件,因此您不会阻止计时器。

  public delegate void TimerEventHandler(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2); ///  /// A multi media timer with millisecond precision ///  /// One event every msDelay milliseconds /// Timer precision indication (lower value is more precise but resource unfriendly) /// delegate to start /// callBack data  /// one event or multiple events /// Dont forget to call timeKillEvent! /// 0 on failure or any other value as a timer id to use for timeKillEvent [DllImport("winmm.dll", SetLastError = true,EntryPoint="timeSetEvent")] static extern UInt32 timeSetEvent(UInt32 msDelay, UInt32 msResolution, TimerEventHandler handler, ref UInt32 userCtx, UInt32 eventType); ///  /// The multi media timer stop function ///  /// timer id from timeSetEvent /// This function stops the timer [DllImport("winmm.dll", SetLastError = true)] static extern void timeKillEvent( UInt32 uTimerID ); TimerEventHandler tim = new TimerEventHandler(this.Link); public void Link(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2) { _counter++; if( (_counter % 10 ) == 0) setLblTxt(); } 

.NET 4内置了用于并行处理的任务调度。

你可以写这样的东西:

 void QueueIt(long tick) { Task workTask = new Task(() => MyMethod()); Task scheduleTask = Task.Factory.StartNew( () => { WaitUtil(tick); // Active waiting workTask.Start(); }); } void WaitUntil(long tick) { var toWait = tick - DateTime.Now.Ticks; System.Threading.Thread.Sleep(toWait); } 

Peter A. Bromberg撰写了一篇关于.NET中高精度代码时序的文章。 轮询DateTime不起作用,因为DateTime.Now具有相对较低的分辨率(大约16ms)。

可能你应该使用System.Threading.Timer如本文所述 ,但根据你的需要,其中描述的其他两个计时器类也可能是有效的选择。

但是,我不知道Windows计时器的精确保证是什么 – 您应该测试它是否足以满足您的需求(并且请记住它可能因机器而异)。

如果您应该在特定时间触发该function,也许您可​​以使用秒表类 。 我认为使用计时器不够安全,因为不能保证已发生事件的时间。

关于什么 :

 public void ReceivesIncomingTCP(DateTime startDate) { Thread.Sleep(startDate.Subtract(DateTime.Now).Milliseconds) DoAction(); }