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

时间:2021-08-29 23:27:23

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

关于微秒级延时函数详细介绍参考:http://blog.csdn.net/a29562268/article/details/68955533