一、SetTimer与timeSetEvent的性能区别
SetTimer和timeSetEvent是两种不同的定时器方法,它们在精确度、使用场景和功能上有所区别:
1)精确度:SetTimer的精确度相对较低,它依赖于操作系统的调度和线程切换机制,因此可能存在一定的延迟;imeSetEvent提供了更高的精确度,它是一种精确度非常高的定时器,不使用容易丢失的窗口消息,而是通过回调函数或事件方式触发,提供高的时间控制精度;
2)使用场景:SetTimer适用于简单的定时任务,如周期性地执行某项操作或单次执行某个函数。它通常用于需要定时触发某些事件或操作的场景(如刷新显示界面等)。timeSetEvent则更适合需要极高精度控制的应用场景,如游戏开发中的精确时间控制、科学计算中的精确计时、精确间隔通信等,可以设置定时器的触发时间间隔和使用的分辨率(精确度),并且支持通过回调函数或事件方式进行触发;
3)功能特点:SetTimer具有较高的灵活性,支持传递额外的参数给要执行的函数,并且可以通过返回的定时器对象进行控制和清除定时器;timeSetEvent则提供了更多的控制选项,包括设置定时器的类型(一次性或周期性触发),并且可以通过timeKillEvent函数来取消定时器。它是微软提供的多媒体定时器API的一部分,用于在非常精确的时间间隔内完成特定事件或函数的调用。
总之:SetTimer和timeSetEvent各有其适用场景和优势。SetTimer适合简单的定时任务和需要传递额外参数的场景,而timeSetEvent则提供了更高的精确度和更多的控制选项,适合需要极高精度控制的应用。
二、函数定义及简单应用
本文介绍的用法都是基于MFC的应用。
1、SetTimer
API函数原型:
UINT_PTR SetTimer(
HWND hWnd, // 窗口句柄
UINT_PTR nIDEvent, // 定时器ID,多个定时器时,可以通过该ID判断是哪个定时器
UINT uElapse, // 时间间隔,单位为毫秒
TIMERPROC lpTimerFunc // 回调函数
);
例:SetTimer(m_hWnd,1,100,TimerFunc); //一个100ms触发一次的定时器.此处NULL在后面加以说明。
在MFC程序中SetTimer被封装在CWnd类中,调用时不用指定窗口句柄,则SetTimer函数的原型变为:
UINT SetTimer(
UINT nIDEvent,
UINT nElapse,
void(CALLBACK EXPORT *lpfnTimer)(HWND,UINT ,YINT ,DWORD)
)
例:SetTimer(2,500,TimerFunc);
该函数通常有两种方式实现:
a) WM_TIMER消息实现:
在MFC例中,第三个参数是一个回调函数,可以设置为NULL,此时使用系统默认的回调函数,系统默认认的是onTime函数.
例:SetTimer(1,1000,NULL);
说明:
1:nIDEvent,计时器名称;
1000: 时间间隔,单位是毫秒;
NULL:使用onTime函数 。
onTime函数可以通过添加WM_TIMER消息来生成函数,在MFC中如图所示:
当不需要计时器的时候调用KillTimer(nIDEvent)来销毁计时器。
BOOL KillTimer(
HWND hWnd, // 窗口句柄,指定要销毁其定时器的窗口
UINT_PTR uIDEvent // 要销毁的定时器的标识符
);
例:KillTimer(1);
b) 调用回调函数
回调函数:
编程分为两类:系统编程(system programming)和应用编程(application programming)。
1)系统编程:简单来说,就是编写库;
2)应用编程:利用写好的各种库来编写具某种功用的程序;
在抽象层的图示里,库位于应用的底下。当程序跑起来时,一般情况下,应用程序(application program)会时常通过API调用库里所预先备好的函数。但是有些库函数(library function)却要求应用先传给它一个函数,好在合适的时候调用,以完成目标任务。这个被传入的、后又被调用的函数就称为回调函数(callback function)。
例:
SetTimer (hwnd, iTimerID, iMsecInterval, TimerProc) ;
在MFC中,hwnd可省略;
TimerProc为回调函数:
void CALLBACK TimerProc(
HWND hwnd, // 窗口句柄,接收定时器消息的窗口
UINT uMsg, // 消息类型,通常为 WM_TIMER
UINT_PTR idEvent, // 定时器的标识符,与 SetTimer 中的 nIDEvent 相对应
DWORD dwTime // 系统时间,定时器超时时的系统时间
);
回调函数可以是全局的,也可以是静态的。静态的可以在类的头文件中以static +返回类型+ CALLBACK+函数(变量)
用例:
//TimerCallback计时器回调函数,
VOID CALLBACK CXXDlg::TimerCallback(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
//GlobalVF::nGPSTime全局变量,GPS通信数据
int year = GlobalVF::nGPSTime.wYear + 2000;
int month = GlobalVF::nGPSTime.wMonth;
int day = GlobalVF::nGPSTime.wDay;
int nh = GlobalVF::nGPSTime.wHour;
int nm = GlobalVF::nGPSTime.wMinute;
int ns = GlobalVF::nGPSTime.wSecond;
GlobalVF::nGPSTime。strTime.Format(_T("%04d.%02d.%02d %02d:%02d:%02d"), year, month, day, nh, nm, ns);
}
2、timeSetEvent
函数:
MMRESULT timeSetEvent(
UINT uDelay, //以毫秒指定事件的周期
UINT uResolution, //以毫秒指定延时的精度, 数值越小定时器事件分辨率越高。缺省值为
//1ms
LPTIMECALLBACK lpTimeProc, //指向一个回调函数
DWORD dwUser, //存放用户提供的回调数据
UINT fuEvent //指定定时器事件类型
)
用例:
UINT nTimeEventID;
DWORD dwUser;
void CALLBACK TimeProc(UINT nTimeEventID,UINT uMsg,DWORD dwUser,DWORD dw1,DWORD dw2)
{
int n;
sockaddr_in RecvAddr;
RecvAddr.sin_family = AF_INET;
RecvAddr.sin_port=htons(net_MaincenCopy_IP.RecPort); //接收数据端口
RecvAddr.sin_addr.s_addr = htonl(net_MaincenCopy_IP.IP_address); //接收数据IP地址
//发送数据
sockaddr_in RecvAddr;
RecvAddr.sin_family = AF_INET;
RecvAddr.sin_port=htons(net_MaincenCopy_IP.RecPort);
RecvAddr.sin_addr.s_addr = htonl(net_MaincenCopy_IP.IP_address);
n=sendto( cencopy_socket_T, //SOCKET cencopy_socket_T,需要在程序中进行初始化并设置
(char*)Cen_MSend, //BYTE Cen_MSend[100]; //发送信息数组,在程序适当地方
//付值
m_dSendN, //发送字节数
0,
(SOCKADDR *) &RecvAddr, //
sizeof(RecvAddr));
}
void CXXView::OnInitialUpdate()
{
.
.
.
nTimeEventID = timeSetEvent(50,1,TimeProc,dwUser,TIME_PERIODIC); //50ms的定时器事件
}
//程序退出时注销计时器
void CXXView::OnClose()
{
// TODO: Add your message handler code here and/or call default
.
.
.
timeKillEvent(nTimeEventID);
CFormView::OnClose();
}
以上程序可以实现精确间隔时间发送数据,曾在本人某应用程序中用到。