我使用Windows多媒体dll创建了一个带
的高分辨率计时器但是timeSetEvent()
页面建议使用:
如何使用CreateTimerQueueTimer()在C#中每10毫秒执行一次方法?
答案 0 :(得分:9)
以下是CreateTimerQueueTimer
的C#包装器的链接:
(向下滚动到示例类的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。