::本篇文章是论述上一篇"基于选择重传ARQ传输协议的数据重传机制方案设计"中定时器的实现
错误更正:
1.背景
在本项目中,由于每个硬件传感器在软件系统中都对应一个传感器对象,数据传输以及重传操作是针对每个传感器对象而言的,所以,每个对象应该对应一个定时器(该定时器是针对每个对象所拥有的重传队列的,并不是针对要接收的下一包,这点很重要,这是两种不同的思维方式,个人认为第一种方案比较好)。针对重传队列的定时器在超时后之后将重传队列发送给对应的传感器,而当接收到数据之后就会重置定时器(即将发送重传队列的时间往后推迟),当一次采集数据的小包都接收完成之后(假设中间掉了几包,如果没有掉包就会立即取消当前定时器并下发重传队列),定时器不会再被重置。这样,过了一段时候后,定时器超时,然后将重传队列发送给发送方。发送方接收到重传队列之后,检测对应bite位,然后重传相应小包数据。
2.面临的关键问题
1. 数据重传模块是工作在幕后,没有跟窗口相关联,所以不能用MFC提供的非常方便的ontimer定时器。
2. 由于每个传感器对象在重传的时候时延不一样,所以不能用一个统一的定时器,必须给每个对象都设置一个单独的定时器。
3. C++类中的线程处理函数必须为static,这使得线程处理函数不能使用类中的非static成员变量。线程处理函数之所以需要是static是因为:设某函数原型为LRESULT ThreadProc(LPVOID pv);若为非静态成员函数,编译时自动展开为 ThreadProc(pClass-> this, pv);与线程函数调用不相符。所以必须使用全局函数或类静态成员函数。
4. 定时器的超时处理函数也必须为static,所以不管你设置多少个定时器,最终定时器的处理函数一样。
3.解决方案
首先对于对于第一个问题,我们使用windows API SetTimer()来自定义定时器。
其次,在开启一个线程时,将当前对象的this指针传递给线程处理函数,在线程处理函数中使用this指针来调用非static的函数并给该对象设置定时器。
最后,我们在超时处理函数中建立一个map,用来记录定时器ID跟相应对象的关系。这样当一个定时器超时后,我们根据ID就能找到对应的对象,从而对该对象进行操作。
下面我们来看一看具体实现代码:
工程下载地址:http://download.csdn.net/detail/pinghegood/4898817
// MyTimer.h: interface for the MyTimer class.
/************************************************************************/
/* author:pinghegood
/* 2012.8.19 20:56 */
/************************************************************************/
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_MYTIMER_H__83EEF423_262A_4A4F_82A0_094336FEAC57__INCLUDED_)
#define AFX_MYTIMER_H__83EEF423_262A_4A4F_82A0_094336FEAC57__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "windows.h"
#pragma warning(disable:4786)//屏蔽map中出现的警告
#include "map"
using namespace std;
class Test;
//保存定时器ID和相应对象this指针的对应关系
typedef map<UINT, Test*> COwnerMap;
class CMyTimer
{
public:
static void DataProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime);
CMyTimer(VOID(*ptimeproc)(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)=NULL);
virtual ~CMyTimer();
void SetMyTimer(UINT nElapse,Test * pcombodata);
void KillMyTimer(bool flag=true);
UINT GetTimerID();
static DWORD WINAPI ThreadRun(LPVOID lpParameter);
private:
DWORD m_idThread;
//保存该实例的定时器标志值
UINT m_nTimerID;
bool m_realclose;
//PTimer m_ptimerproc;// 指向处理函数的指针
/////////////////////////////
static COwnerMap m_ownermap;
static CMyTimer *pthistimer;
Test * m_combodata;
HANDLE m_handle;
UINT m_timeOutTime;
BOOL m_isRunning;
BOOL m_setorkill;
CRITICAL_SECTION m_critivaltime;
CRITICAL_SECTION m_critivalthread;
static CRITICAL_SECTION m_critivalmap;
BOOL SetTimerProc();
};
#endif // !defined(AFX_MYTIMER_H__83EEF423_262A_4A4F_82A0_094336FEAC57__INCLUDED_)
// MyTimer.cpp: implementation of the MyTimer class.
//
//////////////////////////////////////////////////////////////////////
#pragma warning(disable:4786)//屏蔽map中出现的警告
#include "MyTimer.h"
#include "Test.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
COwnerMap CMyTimer::m_ownermap;
CMyTimer *CMyTimer::pthistimer=NULL;
CRITICAL_SECTION CMyTimer::m_critivalmap;
CMyTimer::CMyTimer(VOID(*ptimeproc)(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)): m_nTimerID(-1)
{
InitializeCriticalSection(&m_critivaltime);
InitializeCriticalSection(&m_critivalthread);
InitializeCriticalSection(&m_critivalmap);
//m_ptimerproc=ptimeproc;
m_handle=NULL;
////////////////////////
m_isRunning=true;
m_setorkill=true;
pthistimer=this;
} ;
CMyTimer:: ~CMyTimer(){
// if (NULL!=m_combodata)
// {
// delete m_combodata;
// }
}
//设置定时器,nElapse表示时间间隔
void CMyTimer::SetMyTimer(UINT nElapse,Test * pcombodata){
//保存调用对象指针
m_combodata=pcombodata;
//保存设置超时时间
m_timeOutTime=nElapse;
m_isRunning=true;
m_setorkill=true;
if (NULL==m_handle)
{
//开启时间设置线程
m_handle = CreateThread(NULL,0,ThreadRun,(LPVOID)this,0,&m_idThread);
}
};
//销毁该实例的定时器
void CMyTimer::KillMyTimer(bool flag){
EnterCriticalSection(&m_critivaltime);
m_setorkill=FALSE;
m_timeOutTime=-1;
if (true==flag)
{
UINT MSG=WM_QUIT;
EnterCriticalSection(&m_critivalthread);
m_isRunning=FALSE;
#ifdef MYTIMERDEBUGOUT
TRACE0("=============KillMyTimer1=================");
#endif
LeaveCriticalSection(&m_critivalthread);
PostThreadMessage(m_idThread,MSG,0,0);
//WaitForSingleObject(m_handle,INFINITE);
//CloseHandle(m_handle);
m_handle=NULL;
}
#ifdef MYTIMERDEBUGOUT
TRACE0("==CMyTimerKillMyTimer==");
#endif
LeaveCriticalSection(&m_critivaltime);
m_nTimerID = -1;
};
//获取定时器ID
UINT CMyTimer::GetTimerID(){
return m_nTimerID;
} ;
//设置定时器线程
DWORD WINAPI CMyTimer::ThreadRun(LPVOID lpParameter){
CMyTimer *pThis=reinterpret_cast<CMyTimer*>(lpParameter);
if (pThis->SetTimerProc())
{
#ifdef MYTIMERDEBUGOUT
//TRACE("开始线程");
//TRACE0("ThreadRun Exit Safely!(0xCC66)\n");
#endif
return 0xCC66;
}
else
{
//TRACE0("ThreadRun Exit UnSafely!(0xCC77)\n");
return 0xCC77;
}
};
void CMyTimer::DataProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
EnterCriticalSection(&(pthistimer->m_critivalmap));
Test * tempCR=m_ownermap[idEvent];
LeaveCriticalSection(&(pthistimer->m_critivalmap));
if (NULL!=tempCR)
{
tempCR->TestSetTime();
}
}
BOOL CMyTimer::SetTimerProc()
{
BOOL isRunning=TRUE;
EnterCriticalSection(&m_critivalthread);
isRunning=m_isRunning;
LeaveCriticalSection(&m_critivalthread);
while (isRunning)
{
BOOL openflag=true;
UINT timeOutTime=-1;
EnterCriticalSection(&m_critivaltime);
openflag=m_setorkill;
timeOutTime=m_timeOutTime;
LeaveCriticalSection(&m_critivaltime);
if (TRUE==openflag)
{
MSG msg;
UINT_PTR id=SetTimer(NULL,NULL,m_timeOutTime,(TIMERPROC)(DataProc));
if (m_ownermap.end()!=m_ownermap.find(id))
{
//TRACE1("两次产生的定时器ID一样:%u\n",id);
}
m_ownermap[id]=m_combodata;
m_combodata=NULL;
while ((-1 != GetMessage(&msg,
NULL,
0,
0))&&GetMessage(&msg, NULL, NULL, NULL) != 0)
{
EnterCriticalSection(&m_critivaltime);
openflag=m_setorkill;
LeaveCriticalSection(&m_critivaltime);
if (FALSE==openflag)
{
EnterCriticalSection(&m_critivalmap);
if (!m_ownermap.empty()&&m_ownermap.end()!=m_ownermap.find(id))
{
m_ownermap.erase(id);
}
LeaveCriticalSection(&m_critivalmap);
KillTimer(NULL,id);
//TRACE("killtimertrue");
break;
}
TranslateMessage(&msg); // translates virtual-key codes
DispatchMessage(&msg); // dispatches message to window
Sleep(20);
}
}
Sleep(30);
EnterCriticalSection(&m_critivalthread);
isRunning=m_isRunning;
LeaveCriticalSection(&m_critivalthread);
}
return true;
}
4.运行结果分析
如下图所示:test对象对应的定时器时延为1s,test2对应3s,test3对应5s。由于在第3s时,test的定时器和test2的定时器同时超时,所以他们的输出混在了一起。在第5s时也是同样的道理。正是这样的输出说明了各个对象的定时器是独立工作互不干扰的。
4.相关知识说明