1. 程式人生 > >Windows高精度微秒級(併發)定時器實現

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,500NULL);
TimeRun(2,1000,Test123);
TestKillTimer(1);
//TestKillTimer(2);