介绍
我们在衡量一个函数运行时间,或者判断一个算法的时间效率,或者在程序中我们需要一个定时器,定时执行一个特定的操作,比如在多媒体中,比如在游戏中等,都会用到时间函数。还比如我们通过记录函数或者算法开始和截至的时间,然后利用两者之差得出函数或者算法的运行时间。编译器和操作系统为我们提供了很多时间函数,这些时间函数的精度也是各不相同的,所以,如果我们想得到准确的结果,必须使用合适的时间函数。现在我就介绍windows下的几种常用时间函数。
1:sleep函数
使用:sleep(1000),在windows和linux下1000代表的含义并不相同,windows下的表示1000毫秒,也就是1秒钟;linux下表示1000秒,linux下使用毫秒级别的函数可以使用usleep。
原理:sleep函数是使调用sleep函数的线程休眠,线程主动放弃时间片。当经过指定的时间间隔后,再启动线程,继续执行代码。sleep函数并不能起到定时的作用,主要作用是延时。在一些多线程中可能会看到sleep(0);其主要目的是让出时间片。
精度:sleep函数的精度非常低,当系统越忙它精度也就越低,有时候我们休眠1秒,可能3秒后才能继续执行。它的精度取决于线程自身优先级、其他线程的优先级,以及线程的数量等因素。
2:mfc下的timer事件
使用:1.调用函数settimer()设置定时间隔,如settimer(0,100,null)即为设置100毫秒的时间间隔;2.在应用程序中增加定时响应函数ontimer(),并在该函数中添加响应的处理语句,用来完成时间到时的操作。
原理:同sleep函数一样。不同的是timer是一个定时器,可以指定回调函数,默认为ontimer()函数。
精度:timer事件的精度范围在毫米级别,系统越忙其精度也就越差。
3:c语言下的time
使用:time_t t;time(&t);time函数是获取当前时间。
原理:time函数主要用于获取当前时间,比如我们做一个电子时钟程序,就可以使用此函数,获取系统当前的时间。
精度:秒级别
4:com对象中的coledatetime,coledatetimespan类
使用:coledatetime start_time = coledatetime::getcurrenttime();
coledatetimespan end_time = coledatetime::getcurrenttime()-start_time;
while(end_time.gettotalseconds() < 2)
{
// 处理延时或定时期间能处理其他的消息
dosomething()
end_time = coledatetime::getcurrenttime-start_time;
}
原理:以上代表延时2秒,而这两秒内我们可以循环调用dosomething(),从而实现在延时的时候我们也能够处理其他的函数,或者消息。coledatetime,coledatetimespan是mfc中ctime,ctimespan在com中的应用,所以,上面的方法对于ctime,ctimespa同样有效。
精度:秒级别
5:c语言下的时钟周期clock()
使用: clock_t start = clock();
sleep(100);
clock_t end = clock();
double d = (double)(start - end) / clocks_per_sec;
原理:clock()是获取计算机启动后的时间间隔。
精度:ms级别,对于短时间内的定时或者延时可以达到ms级别,对于时间比较长的定时或者延迟精度还是不够。在windows下clocks_per_sec为1000。
6:windows下的gettickcount()
使用: dword start = gettickcount();
sleep(100);
dword end = gettickcount();
原理:gettickcount()是获取系统启动后的时间间隔。通过进入函数开始定时,到退出函数结束定时,从而可以判断出函数的执行时间,这种时间也并非是函数或者算法的真实执行时间,因为在函数和算法线程不可能一直占用cpu,对于所有判断执行时间的函数都是一样,不过基本上已经很准确,可以通过查询进行定时。gettickcount()和clock()函数是向主板bios要real time clock时间,会有中断产生,以及延迟问题。
精度:windowsnt 3.5以及以后版本精度是10ms,它的时间精度比clock函数的要高,gettickcount()常用于多媒体中。
7:windows下timegettime
使用:需要包含mmsystem.h,windows.h,加入静态库winmm.lib.
timebeginperiod(1);
dword start = timegettime();
dword end = timegettime();
timeendperiod(1);
原理:timegettime也时常用于多媒体定时器中,可以通过查询进行定时。通过查询进行定时,本身也会影响定时器的定时精度。
精度:毫秒,与gettickcount()相当。但是和gettickcount相比,timegettime可以通过timebeginperiod,timeendperiod设置定时器的最小解析精度, timebeginperiod,timeendperiod必须成对出现。
8:windows下的timesetevent
使用:还记的vc下的timer吗?timer是一个定时器,而以上我们提到几种时间函数或者类型,实现定时功能只能通过轮训来实现,也就是必须另外创建一个线程单独处理,这样会影响定时精度,好在windows提供了内置的定时器timesetevent,函数原型为
mmresult timesetevent( uint udelay, //以毫秒指定事件的周期
uint uresolution, //以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms
lptimecallback lptimeproc, //指向一个回调函数
word dwuser, //存放用户提供的回调数据
uint fuevent )// 标志参数,time_oneshot:执行一次;time_periodic:周期性执行
具体应用时,可以通过调用timesetevent()函数,将需要周期性执行的任务定义在 lpfunction回调函数中(如:定时采样、控制等),从而完成所需处理的事件。需要注意的是:任务处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后,应及时调用timekillevent()将之释放。
原理:可以理解为代回调函数的timegettime
精度:毫秒,timesetevent可以通过timebeginperiod,timeendperiod设置定时器的最小解析精度, timebeginperiod,timeendperiod必须成对出现。
9:高精度时控函数queryperformancefrequency,queryperformancecounter
使用:large_integer m_nfreq;
large_integer m_nbegintime;
large_integer nendtime;
queryperformancefrequency(&m_nfreq); // 获取时钟周期
queryperformancecounter(&m_nbegintime); // 获取时钟计数
sleep(100);
queryperformancecounter(&nendtime);
cout << (nendtime.quadpart-m_nbegintime.quadpart)*1000/m_nfreq.quadpart << endl;
原理:cpu上也有一个计数器,以机器的clock为单位,可以通过rdtsc读取,而不用中断,因此其精度与系统时间相当。
精度:计算机获取硬件支持,精度比较高,可以通过它判断其他时间函数的精度范围。
10小结:以上提到常用的9种时间函数,由于他们的用处不同,所以他们的精度也不尽相同,所以如果简单的延时可以用sleep函数,稍微准确的延时可以使用clock函数,gettickcount函数,更高级的实用timegettime函数;简单的定时事件可以用timer,准确地可以用timesetevent;或取一般系统时间可以通time,或者ctime,或者coledatetime,获取准确的时间可以用clock,或者gettickcount函数,或者timegettime函数,而获取准确地系统时间要使用硬件支持的queryperformancefrequency函数,queryperformancecounter函数。