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

  自从上次封装微秒延时函数后,利用空闲时间试着封装一个微秒定时器(类似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!

发布了94 篇原创文章 · 获赞 206 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/a29562268/article/details/70037639