如何使用CreateTimerQueueTimer在C#中创建高分辨率计时器?

时间:2009-06-18 23:46:23

标签: c# timer

我使用Windows多媒体dll创建了一个带

的高分辨率计时器

timSetEvent()

但是timeSetEvent()页面建议使用:

CreateTimerQueueTimer()

如何使用CreateTimerQueueTimer()在C#中每10毫秒执行一次方法?

3 个答案:

答案 0 :(得分:9)

以下是CreateTimerQueueTimer的C#包装器的链接:

http://social.msdn.microsoft.com/Forums/en-CA/csharpgeneral/thread/822aed2d-dca0-4a8e-8130-20fab69557d2

(向下滚动到示例类的Hobz的最后一篇文章)

我只是自己试了一下,它运行正常。但是,您需要添加的一件事是在启动计时器之前调用timeBeginPeriod(1)以便将系统设置为高分辨率。 timeSetEvent在内部调用timeBeginPeriod,这就是为什么有些人错误地认为它会创建一个更高分辨率的计时器。

答案 1 :(得分:3)

传递给CreateTimerQueueTimer的回调应该是一个非托管函数,该函数将在回调的生命周期内存在。托管代表可以在内存中移动the underlying stub created by the marshalling will not do this so there is no need to pin the delegate。但是,由于来自非托管代码的指针不足以使其保持活动状态,因此需要保持委托不被垃圾收集。因此,您必须确保通过维护某个托管引用来保持委托(可能通过使用GCHandle

传递给回调函数的PVOID参数必须在内存中修复,因为非托管函数希望它在函数返回后不会移动。在.Net中,这种固定自动发生(有效),但仅限于被调用函数的生命周期。因此,如果您使用对某个托管对象的引用(例如通过获取IntPtr),则必须固定基础对象(同样,GCHandle可以以稍微不同的方式用于此)。要查看这是否是问题,请尝试使用IntPtr.Zero作为参数来测试它是否有效。

如果这解决了问题,你需要在非托管堆中将参数分配为原始字节(并相应地编组),使用一些可安全放入PVOID(如Int32)大小的blittable类型或使用上面的GCHandle技术可以维护一个指向托管实例的稳定指针,如果做错了,这将产生重大的性能影响。

答案 2 :(得分:2)

最好使用timeSetEvent,因为它的结果更加一致。在平均现代硬件上,对于小间隔,间隔长度的偏差比使用CreateTimerQueueTimer时小十倍。这假设您在调用CreateTimerQueueTimer之前没有忘记增加计时器分辨率,否则差异会更大。所以请改用timeSetEvent。