Windows精确定时(ms)

最近想模拟个波形发生器,查阅了一下windows的精确定时能力.

精确定时的用途基本上分为两种:延时;定时.

支配性参数--Timer Resolution

操作系统任务切换有时间片的概念,即每到达一个时间片 系统就可以调度一次,来完成任务切换及多个线程的分时复用;

该时间片也就是操作系统运行时的时间分辨率:该值并不固定,可以通过timeGetDevCaps 来获取,得到的时间片是毫秒(ms)级别.

可以修改时间片来提高Thread.Sleep()及定时器的精度.修改方法见MSDN Timer Resolution .

实际上可以通过一些Windows没有公开的API将时间片提高到微秒(us)级别,类似于timeGetDevCaps的对应API为NtQueryTimerResolution,修改时间片的对应API为NtSetTimerResolution .详细描述见Microsecond Resolution Time Services for Windows.

使用方法如下:

 1 typedef NTSTATUS (CALLBACK* NTSETTIMERRESOLUTION)
 2 (
 3          IN ULONG DesiredTime,
 4          IN BOOLEAN SetResolution,
 5          OUT PULONG ActualTime
 6 );
 7 NTSETTIMERRESOLUTION NtSetTimerResolution;
 8 
 9 typedef NTSTATUS (CALLBACK* NTQUERYTIMERRESOLUTION)
10 (
11         OUT PULONG MaximumTime,
12         OUT PULONG MinimumTime,
13         OUT PULONG CurrentTime
14 );
15 NTQUERYTIMERRESOLUTION NtQueryTimerResolution;
16 
17 void QueryTimerResolution(void){
18 
19     HMODULE hNtDll = LoadLibrary(TEXT("NtDll.dll"));
20     if (hNtDll)
21     {
22             NtQueryTimerResolution = (NTQUERYTIMERRESOLUTION)GetProcAddress(hNtDll,"NtQueryTimerResolution");
23             NtSetTimerResolution = (NTSETTIMERRESOLUTION)GetProcAddress(hNtDll,"NtSetTimerResolution");
24             FreeLibrary(hNtDll);
25     }
26     if (NtQueryTimerResolution == NULL || NtSetTimerResolution  ==  NULL){
27         printf("Search function failed!\n");
28         return ;
29     }
30             
31     NTSTATUS nStatus;
32 
33     ULONG Min=0;
34     ULONG Max=0;
35     ULONG Cur=0;
36     nStatus = NtQueryTimerResolution(&Max, &Min,&Cur);
37 
38     printf("NtQueryTimerResolution -> \nMax=%lu(100ns) Min=%lu(100ns) Cur=%lu(100ns)\n",Min,Max,Cur);
39 
40     //BOOL bSetResolution = TRUE;
41     //ULONG nActualTime;
42     //ULONG nDesiredTime = 20064;
43     //nStatus =  NtSetTimerResolution (nDesiredTime, bSetResolution,&nActualTime);
44 }

有个小软件(Timer Resolution)可以获取并设置为最大时间分辨率.

精确延时

对应于精确延时可以采用QueryPerformanceCounterQueryPerformanceFrequency;

前一个函数用来获取性能计数器值,后一个函数来获取性能计数器的频率.将所需延时转换成对应的性能计数器差值,然后不断查询,等到延时时间到达.这也是Windows操作系统所能达到的“最精确的延时".

当然,也可以通过其他途径来比较时间获取完成延时功能:

在文章Implement a Continuously Updating, High-Resolution Time Provider for Windows 中对可以获取当前毫秒(ms)及的时间函数做了比较,由于函数底层实现的不同,采用QueryPerformanceCounter函数运行时间较长.GetSystemTimeAsFileTime是其中执行时间最短的函数.但是并没有比较GetTickCount.

在文章APIs you never heard of - the Timer APIs的评论中,写到:

Centaur, QPC is heavyweight, and is documented as such. timeGetTime is faster but much less accurate. 

I actually checked the code for timeGetTime and GetTickCount(). GetTickCount() takes the number of clock interrupts and multiplies it by the clock frequency. timeGetTime() reads a field called the "interrupt time", which is updated periodically by the kernel (I wasn't able to find out how frequently).

精确定时

对于精确定时可选的方式有(WinAPI):

在文章On WinAPI timers and their resolution中,作者对这三种方式都进行了对比,并以性能计数器为参考,得出结论

timeSetEvent定时最为精确,误差较小,CreateTimerQueueTimer次之,SetTimer最差.

 

对于多任务的Windows来讲,程序执行过程中不能保证不会发生任务切换的情况,故而,精确定时/延时都不会得到保证,就如文章Implement a Continuously Updating, High-Resolution Time Provider for Windows最后的结论.

 Just don't perform anything requiring real-time predictability on the basis of time stamps in Windows NT.

 

当然,对于定时/延时要求并不非常苛刻的情况下,得到ms+级别的精度依然是可能的.采用前文所述调整系统时间分辨率(针对部分计算机可达到0.5ms甚至更低),误差会在一个或者数个时间分辨率.

总结

综上所述:

如果进行定时操作,可以采用timeSetEvent或者CreateTimerQueueTimer,后者相对更为灵活.

如果进行延时操作,试图延时在ms一下,采用QueryPerformanceCounter的方式;在ms级别可以采用timeGetTime和GetSystemTimeAsFileTime,抑或GetTickCount.或者说简简单单使用Thread.Sleep(),针对MSDN所述其典型误差在20-60ms,这是对于XP且系统时间分辨率过大来讲的.

如果试图在Windows上得到更为精确的延时/定时,尝试使用Timer Resolution或者前文所述的方法增大系统时间分辨率,但是系统任务调度等都要付出性能代价,增大系统时间分辨率可能会导致性能方面的损耗.这方面也值得考量.

 参考

来源:

转载于:https://www.cnblogs.com/liff-engineer/archive/2013/06/06/3121432.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值