您的位置:首页 > 其它

Windows高精度微秒级(并发)定时器实现

2017-04-10 21:35 288 查看
自从上次封装微秒延时函数后,利用空闲时间试着封装一个微秒定时器(类似MFC定时器形式)使用起来效果还不错。

关于定时器的几点介绍:

1.设计采用了自动释放定时器节点方式(增加虚析构函数在内部做相关释放判断,即使用完不释放节点也没关系);

2.设计采用了双向链表方式做定时器节点(为了方便起见,没有采用环形双向链表);

3.增加了第三参数为回调函数(采用MFC风格,如果第三个参数不为空,超时后调用回调函数,否则调用默认函数);

4.设计采用了并行方式(可以同时启动多个ID不相同的定时器,并且每个定时器之间没有影响);

5.释放采用了先退出定时器线程,后删除节点风格(防止数据异常造成崩溃)。

函数定义如下:

//参数一 定时器ID号,参数二 定时时间(微秒),参数三 回调函数(不需要设置NULL或者不写)
int TimeRun(int id,int outtime,void (CALLBACK* lpfnTimer)(/*HWND, UINT, UINT_PTR, DWORD*/) = NULL);//定时器启动函数

//参数一 定时器ID号
void TestKillTimer(int id);//定时器销毁函数 (可以不使用)

void CALLBACK Test123();//测试写的回调函数 函数名称及参数可以自己定 与定时器内以及定时器结构体的回调函数配套

//第一个参数 定时器ID号(ID号用于多个定时器之间的判断)
void TestTime(int id);//测试用的普通定时器调用函数 想启动的函数写在这个函数内

DWORD WINAPI TimerRunThread(LPVOID _this);//定时器线程函数

typedef struct timest//定时器数据保存结构体
{
int id;//ID号
int outtime;//超时时间
volatile int beginRun;//运行状态
void (CALLBACK* lpfnTimer)(/*HWND, UINT, UINT_PTR, DWORD*/);//回调函数
timest *prev,*next;//双向链表指针
}*ptimest;
typedef struct Testtimest//定时器执行结构体
{
ptimest ptime,ptimehead;//定时器运行节点以及头节点
Testtimest()
{
ptime = 0;
ptimehead = 0;
}
virtual ~Testtimest()//析构函数内部封装了定时器对象退出后的检查及释放
{
while (0 != ptimehead)
{
ptimest deletime;
deletime = ptimehead->next;
if (1 == ptimehead->beginRun)//如果定时线程还在执行 ,先退出线程再删除
{
ptimehead->beginRun = 0;
UsSleep(ptimehead->outtime * 2);//删除线程节点前要留出当前定时器的2倍睡眠时间
}
delete ptimehead;
ptimehead = deletime;
}
}
};
Testtimest testtime;


代码实现如下:

int TimeRun(int id,int outtime,void (CALLBACK* lpfnTimer)(/*HWND, UINT, UINT_PTR, DWORD*/))//定时器启动实现 采用了可以多次重复创建并且删除的安全方式
{
if (0 > id || 0 >= outtime)
{
return -1;
}
testtime.ptime = testtime.ptimehead;//为方便多次删除后继续用,每次先获取首节点
if (0 != testtime.ptime)//如果头节点不为空,判断当前新增定时器ID是否与之前增加ID重复
{
while (id != testtime.ptime->id && 0 != testtime.ptime->next)
testtime.ptime = testtime.ptime->next;
if (id == testtime.ptime->id)//如果启动的定时器Id与之前的重复 则不启动
{
return -1;
}
}
if (0 == testtime.ptime)
{
testtime.ptime = new timest;
testtime.ptime->next = 0;
testtime.ptime->prev = 0;
testtime.ptimehead = testtime.ptime;
}
else
{
while (0 != testtime.ptime->next)
testtime.ptime = testtime.ptime->next;
testtime.ptime->next = new timest;
testtime.ptime->next->next = 0;
testtime.ptime->next->prev = testtime.ptime;
}
while (0 != testtime.ptime->next)
testtime.ptime = testtime.ptime->next;
testtime.ptime->id = id;//id
testtime.ptime->beginRun = 1;//启动状态
testtime.ptime->outtime = outtime;//超时时间
testtime.ptime->lpfnTimer = lpfnTimer;//回调函数
HANDLE handle = CreateThread(NULL,0,TimerRunThread,testtime.ptime,0,NULL);
CloseHandle(handle);//为了C程序也能运行采用了API线程,并且句柄没实际用处就直接释放掉
return 0;
}

void CALLBACK Test123()//测试回调函数实现
{
int a;
a = 2;
}

DWORD WINAPI TimerRunThread(LPVOID _this)//定时器线程函数实现
{
ptimest Runject = (/*decltype(_this)*/ptimest)_this;//本来想用获取类型方式来直接获取传入的类型,后来想到了在线程函数中类型是void*所以只能手动写入传入的参数类型
while (1 == Runject->beginRun)//定时器状态为1执行,0时退出线程函数,删除定时器节点
{
UsSleep(Runject->outtime);
if (0 != Runject->lpfnTimer)//如果回调函数不为空调用回调函数
{
Runject->lpfnTimer();
}
else
{
TestTime(Runject->id);//根据ID号进行调用普通定时器启动函数
}
}
return 0;
}

void TestTime(int id)//测试 定时器调用函数实现
{
int a = 0;
if (1 == id)
{
a = 1;
}
if (2 == id)
{
a = 2;
}
if (3 == id)
{
a = 3;
}
}
void TestKillTimer(int id)//定时器销毁函数实现
{
ptimest deletime = testtime.ptimehead;
ptimest next,prev;
if (id == deletime->id)//首节点
{
next = testtime.ptimehead->next;
deletime->beginRun = 0;
UsSleep(deletime->outtime * 2);//为了可以退出定时器线程并且安全删除定时器节点采用了2倍超时时间
delete deletime;
deletime = 0;
testtime.ptimehead = next;
if (0 != next)
{
next->prev = 0;
}
return ;
}
while (id != deletime->id && 0 != deletime)
deletime = deletime->next;
if (0 == deletime)//没有找到Id
{
return ;
}
prev = deletime->prev;
next = deletime->next;
deletime->beginRun = 0;
UsSleep(deletime->outtime * 2);
delete deletime;
deletime = 0;
prev->next = next;
if (0 != next)
{
next->prev = prev;
}
}

int UsSleep(int us)//微秒延时函数实现
{
LARGE_INTEGER fre;
if (QueryPerformanceFrequency(&fre))
{
LARGE_INTEGER run,priv,curr,res;
run.QuadPart = fre.QuadPart * us / 1000000;
QueryPerformanceCounter(&priv);
do
{
QueryPerformanceCounter(&curr);
} while (curr.QuadPart - priv.QuadPart < run.QuadPart);
curr.QuadPart -= priv.QuadPart;
int nres = (curr.QuadPart * 1000000 / fre.QuadPart);
return nres;
}
return -1;//
}


使用方式:

TimeRun(1,500,NULL);
TimeRun(2,1000,Test123);
TestKillTimer(1);
//TestKillTimer(2);


关于微秒级延时函数详细介绍参考:http://blog.csdn.net/a29562268/article/details/68955533
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息