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!

發佈了97 篇原創文章 · 獲贊 218 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章