当我们在衡量一个函数的运行时间,或者是判断一个算法的时间效率,或者在程序中我们需要一个定时器来定时执行一个特定的操作,如闹铃、时钟、多媒体、游戏动画等,都要用到时间函数。编译器和时间函数为我们提供了很多时间函数,而这些函数的精度和用法也是各不相同的。下面我们就对几种常见的定时函数进行比较、详述:
1、 Sleep函数
用法
|
精度
|
原理及说明
|
备注
|
|
其精度很低,系统越忙,精度越低,其精度取决于当前线程及其他线程的优先级以及线程数量等因素
|
Sleep
函数是使调用Sleep函数的线程休眠,线程主动放弃时间片。当经过指定的时间间隔后,再启动线程,继续执行代码。Sleep函数并不能起到定时的作用,主要作用是延时。在一些多线程中可能会看到Sleep(0);其主要目的是让出时间片。
|
|
|
当你要创建一个窗口是使用这个函数必须要小心。因为当一个进程创建一个窗口时,它必须处理消息,而消息广播被发送到系统所有的窗口中,这时你如果在一个线程中使用了
Sleep
函数来无限延迟,可能会导致系统死锁。所以在此时你可以应用函数
MsgWaitForMultipleObjects
或
MsgWaitForMultipleObjectsEx
来代替
Sleep
(
MSDN
)
|
2、 MFC中的timer事件
用法
|
精度
|
原理及说明
|
备注
|
先使用SetTimer()来定时
再在程序中增加定时响应函数OnTimer()并在此函数中增加相应的处理语句完成时间到时的操作
|
精度在毫秒级别,一般其响应时间为55ms,且系统越忙精度越差。
|
原理与Sleep一样,不同的是timer是一个定时器,可以指定回调函数,默认为OnTimer().
|
常用于做电子时钟或者在游戏动画中使用它。
|
3、 C中的Time应用
用法
|
精度
|
原理及说明
|
备注
|
time_t t;
time(&t);
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; |
ms级别,对于短时间内的定时或者延时可以达到ms级别,对于时间比较长的定时或者延迟精度还是不够。
|
原理:clock()是获取计算机启动后的时间间隔。
|
在windows下
CLOCKS_PER_SEC为1000。
|
6、 Windows下的GetTickCount的使用
用法
|
精度
|
原理及说明
|
备注
| ||||||
DWORD start = GetTickCount();
Sleep(100); DWORD end = GetTickCount(); |
其时间精度要比clock高,
|
GetTickCount()是获取系统启动后的时间间隔。通过进入函数开始定时,到退出函数结束定时,从而可以判断出函数的执行时间,这种时间也并非是函数或者算法的真实执行时间,因为在函数和算法线程不可能一直占用CPU,对于所有判断执行时间的函数都是一样,不过基本上已经很准确,可以通过查询进行定时。GetTickCount()和Clock()函数是向主板BIOS要real time clock时间,会有中断产生,以及延迟问题。
|
常用于多媒体
|
7、 Windows下的timeGetTime使用
用法
|
精度
|
原理及说明
|
备注
|
timeBeginPeriod(1);
DWORD start = timeGetTime(); Sleep(100); DWORD end = timeGetTime(); timeEndPeriod(1); |
毫秒
与GetTickCount()相当。但是和GetTickCount相比,timeGetTime可以通过timeBeginPeriod,timeEndPeriod设置定时器的最小解析精度
|
timeGetTime也时常用于多媒体定时器中,可以通过查询进行定时。通过查询进行定时,本身也会影响定时器的定时精度。
|
timeBeginPeriod,
timeEndPeriod必须成对出现。
需要包含Mmsystem.h,Windows.h,加入静态库Winmm.lib
|
Windows CE 下需要包含需要包含Mmsystem.h,Windows.h,
加入静态库mmtimer.lib
|
8、 Windows 下的TimeSetEvent函数的使用
用法
|
精度
|
原理及说明
|
备注
|
需要调用:
timeGetDevCaps() timeBeginPeriod() 然后调用 TimeSetEvent() 最后调用:timeKillEvent() 定义一个回调函数: static VOID CALLBACK StopwatchTimer(
UINT uTimerID,
UINT uMsg,
DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
|
毫秒
|
Timer是一个定时器,而以上我们提到几种时间函数或者类型,实现定时功能只能通过轮训来实现,也就是必须另外创建一个线程单独处理,这样会影响定时精度,好在windows提供了内置的定时器timeSetEvent。 具体应用时,可以通过调用timeSetEvent()函数,将需要周期性执行的任务定义在 lpFunction回调函数中(如:定时采样、控制等),从而完成所需处理的事件。需要注意的是:任务处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后,应及时调用timeKillEvent()将之释放。原理:可以理解为代回调函数的timeGetTime
|
timeBeginPeriod
,
timeEndPeriod置定时器的最小解析精度必须成对出现。
需要包含Mmsystem.h,Windows.h,加入静态库Winmm.lib
|
Windows CE 下需要包含需要包含Mmsystem.h,Windows.h,
加入静态库mmtimer.lib
|
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读取,而不用中断,因此其精度与系统时间相当。
|
|
小结:以上提到的9种时间函数,由于他们的用处不同,其精度也有很大的差异,用户可以根据不同的需求选择合适的时间函数。
本文原始资料来源于网络,经个人加工形成,有些函数由于没有经过自己的测试,资料不一定准确,在选择了具体的函数之后,使用时尚须查阅该函数具体的资料。
我在程序中使用过的函数举例:
1:Sleep(1000)
//延时1s,多用于多线程程序中,如果在单个线程的程序中使用,有类似死机现象。
2、Timer事件:
该函数使用比较简单,但由于其精度与系统繁忙程度有关,在不需精确定时的场合应用非常好用,但如果需要精确定时,建议不要使用;
(1) 定时 setTimer(1,1000,NULL)//为定时器一设置一个1秒的周期
(2) 在应用程序中添加OnTime()
{
CDC * pDC;
pDC->TextOut(100,100,“This is a test!”);
}//消息响应函数,在该函数中处理你想有响应的事件。
3、TimeSetEvent()函数
//
加入静态链接库Winmm.lib(Windows xp),mmTimer.lib(Windows CE)
#include <Mmsystem.h>
//
已经包含了的头文件
#include <Windows.h>
# define ONE_MILLI_SECOND
100
//
定义1ms和2s时钟间隔,以ms为单位 ;
# define TWO_SECOND
2000
# define TIMER_ACCURACY
1
//
定义时钟分辨率,以ms为单位
UINT wTimerRes_1ms,wTimerRes_2s;
//
定义时间间隔
UINT wAccuracy;
//
定义分辨率
UINT TimerID_1ms,TimerID_2s;
//
定义定时器句柄
static
VOID CALLBACK OneMilliSecondProc(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
{
//
定义计数器
static
int
ms =
0
;
CDAQView *app = (CDAQView *)dwUser;
//
取得系统时间,以ms为单位
DWORD osBinaryTime = GetTickCount();
//
输出计数器值和当前系统时间
app->Timer();
}
CDAQView::CDAQView()
{
// TODO: add construction code here
//
给时间间隔变量赋值
wTimerRes_1ms = ONE_MILLI_SECOND;
wTimerRes_2s = TWO_SECOND;
TIMECAPS tc;
//
利用函数timeGetDevCaps取出系统分辨率的取值范围,如果无错则继续;
if
(timeGetDevCaps(&tc,
sizeof
(TIMECAPS))==TIMERR_NOERROR)
{
wAccuracy=min(max(tc.wPeriodMin,
//
分辨率的值不能超出系统的取值范围
TIMER_ACCURACY),tc.wPeriodMax);
//
调用timeBeginPeriod函数设置定时器的分辨率
timeBeginPeriod(wAccuracy);
}
CDAQView::~CDAQView()
{
timeKillEvent(TimerID_1ms);
//
删除两个定时器
//
删除设置的分辨率
timeEndPeriod(wAccuracy);
}
void
CDAQView::StartOneMilliSecondTimer()
{
if
((TimerID_1ms = timeSetEvent(wTimerRes_1ms, wAccuracy,
(LPTIMECALLBACK) OneMilliSecondProc,
//
回调函数;
(DWORD)
this
,
//
用户传送到回调函数的数据;
TIME_PERIODIC)==
10
))
//
周期调用定时处理函数;
{
AfxMessageBox(
"
不能进行定时!"
, MB_OK | MB_ICONASTERISK);
}
else
{
}
//
不等于0表明加装成功,返回此定时器的句柄;
}
void
CDAQView::InitializeTimer()
{
StartOneMilliSecondTimer();
}
CDAQView::Timer()
{
}