自從上次封裝微秒延時函數後,利用空閒時間試着封裝一個微秒定時器(類似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!