精华内容
下载资源
问答
  • c++定时器

    2019-03-14 10:33:00
    定时器作为常用的组件,通常的实现方式有几种:链表,最小堆,时间轮,等等。 1 链表 性能比较弱,适用于简单的场景,查询、插入、删除效率都不高 2 最小堆 性能比较高,适用于定时器需求较多的场景 3 时间轮 ...

    定时器作为常用的组件,通常的实现方式有几种:链表,最小堆,时间轮,等等。

    1 链表

    性能比较弱,适用于简单的场景,查询、插入、删除效率都不高

    2 最小堆

    性能比较高,适用于定时器需求较多的场景

    3 时间轮

    性能比较高,适用于定时器需求很大的场景

     

        在网上查了一些资料,真正可以直接用的代码并不多,不可以直接运行,就不能给读者以更加感性的认识,也就不愿意更多的去分析代码和学习。所以,以 https://www.cnblogs.com/junye/p/5836552.html 为例,将它的最小堆实现,做了一些修改,可以接在gcc 4.8.2以上的环境编译通过并运行。将代码保存为 a.cc,编译命令为

    g++ -g -o a a.cc -std=c++11
    

    具体的代码如下:

    #include <iostream>
    #include <chrono>
    #include <thread>
    #include <vector>
    #include <sys/time.h>
    
    // global declaration
    typedef void (*Fun)(void);
    
    class Timer;
    class TimerManager;
    
    // header file for Timer
    class Timer
    {
    public:
        enum TimerType { ONCE, CIRCLE };
    
        Timer(TimerManager& manager);
        ~Timer();
    
        void Start(Fun fun, unsigned interval, TimerType timeType = CIRCLE);
        void Stop();
    
    private:
        void OnTimer(unsigned long long now);
    
    private:
        friend class TimerManager;
        TimerManager& manager_;
        TimerType timerType_;
        Fun timerFun_;
        unsigned interval_;
        unsigned long long expires_;
    
        int heapIndex_;
    };
    
    // header file for TimerManager
    class TimerManager
    {
    public:
        static unsigned long long GetCurrentMillisecs();
        void DetectTimers();
    
    private:
        friend class Timer;
        void AddTimer(Timer* timer);
        void RemoveTimer(Timer* timer);
    
        void UpHeap(int index);
        void DownHeap(int index);
        void SwapHeap(int, int index2);
    
    private:
        struct HeapEntry
        {
            unsigned long long time;
            Timer* timer;
        };
        std::vector<HeapEntry> heap_;
    };
    
    // implemetation of Timer
    
    Timer::Timer(TimerManager& manager)
        : manager_(manager)
        , heapIndex_(-1)
    {
        // to-do
    }
    
    Timer::~Timer()
    {
        Stop();
    }
    
    inline void Timer::Start(Fun fun, unsigned interval, TimerType timeType)
    {
        Stop();
        interval_ = interval;
        timerFun_ = fun;
        timerType_ = timeType;
        this->expires_ = this->interval_ + TimerManager::GetCurrentMillisecs();
        manager_.AddTimer(this);
    }
    
    void Timer::Stop()
    {
        if (heapIndex_ != -1)
        {
            manager_.RemoveTimer(this);
            heapIndex_ = -1;
        }
    }
    
    void Timer::OnTimer(unsigned long long now)
    {
        if (timerType_ == Timer::CIRCLE)
        {
            expires_ = interval_ + now;
            manager_.AddTimer(this);
        }
        else
        {
            heapIndex_ = -1;
        }
        timerFun_();
    }
    
    // implemetation of TimerManager
    
    void TimerManager::AddTimer(Timer* timer)
    {
        timer->heapIndex_ = heap_.size();
        HeapEntry entry = { timer->expires_, timer };
        heap_.push_back(entry);
        UpHeap(heap_.size() - 1);
    }
    
    void TimerManager::RemoveTimer(Timer* timer)
    {
        int index = timer->heapIndex_;
        if (!heap_.empty() && index < heap_.size())
        {
            if (index == heap_.size() - 1)
            {
                heap_.pop_back();
            }
            else
            {
                SwapHeap(index, heap_.size() - 1);
                heap_.pop_back();
                int parent = (index - 1) / 2;
                if (index > 0 && heap_[index].time < heap_[parent].time)
                {
                    UpHeap(index);
                }
                else
                {
                    DownHeap(index);
                }
            }
        }
    }
    
    void TimerManager::DetectTimers()
    {
        unsigned long long now = GetCurrentMillisecs();
    
        while (!heap_.empty() && heap_[0].time <= now)
        {
            Timer* timer = heap_[0].timer;
            RemoveTimer(timer);
            timer->OnTimer(now);
        }
    }
    
    void TimerManager::UpHeap(int index)
    {
        int parent = index >> 1;
        while (index > 0 && heap_[index].time < heap_[parent].time)
        {
            SwapHeap(index, parent);
            index = parent;
            parent = index >> 1;
        }
    }
    
    void TimerManager::DownHeap(int index)
    {
        int child = (index << 1) + 1;
        while (child < heap_.size())
        {
            int minChild = (child + 1 == heap_.size() || heap_[child].time < heap_[child + 1].time)? child : child + 1;
            if (heap_[index].time < heap_[minChild].time)
                break;
            SwapHeap(index, minChild);
            index = minChild;
            child = (index << 1) + 1;
        }
    }
    
    void TimerManager::SwapHeap(int index1, int index2)
    {
        HeapEntry tmp = heap_[index1];
        heap_[index1] = heap_[index2];
        heap_[index2] = tmp;
        heap_[index1].timer->heapIndex_ = index1;
        heap_[index2].timer->heapIndex_ = index2;
    }
    
    unsigned long long TimerManager::GetCurrentMillisecs()
    {
        timeval tv;
        ::gettimeofday(&tv, 0);
        unsigned long long ret = tv.tv_sec;
        return ret * 1000 + tv.tv_usec / 1000;
    }
    
    // test code
    
    void TimerHandler_1()
    {
        std::cout << "TimerHandler_1" << std::endl;
    }
    
    void TimerHandler_2()
    {
        std::cout << "TimerHandler_2" << std::endl;
    }
    
    void TimerHandler_3()
    {
        std::cout << "TimerHandler_3" << std::endl;
    }
    
    void TimerHandler_4()
    {
        std::cout << "TimerHandler_4" << std::endl;
    }
    
    int main()
    {
        TimerManager tm;
    
        Timer t1(tm);
        t1.Start(&TimerHandler_1, 1000);
    
        Timer t2(tm);
        t2.Start(&TimerHandler_2, 500);
    
        Timer t3(tm);
        t3.Start(&TimerHandler_3, 1500);
    
        Timer t4(tm);
        t4.Start(&TimerHandler_4, 100);
    
        while (true)
        {
            tm.DetectTimers();
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
        return 0;
    }
    

      

     

    转载于:https://www.cnblogs.com/warnet/p/10528650.html

    展开全文
  • C++定时器

    2017-10-24 13:46:39
    1.1 用WM_TIMER来设置定时器  先请看SetTimer这个API函数的原型  UINT_PTR SetTimer(  HWND hWnd, // 窗口句柄  UINT_PTR nIDEvent, // 定时器ID,多个定时器时,可以通过该ID判断是哪个定时器...
    1.1   用WM_TIMER来设置定时器 

    先请看SetTimer这个API函数的原型 

    UINT_PTR   SetTimer( 
    HWND   hWnd,   //   窗口句柄 
    UINT_PTR   nIDEvent,   //   定时器ID,多个定时器时,可以通过该ID判断是哪个定时器 
    UINT   uElapse,   //   时间间隔,单位为毫秒 
    TIMERPROC   lpTimerFunc   //   回调函数 
    ); 

    例如 
    SetTimer(m_hWnd,1,1000,NULL);   //一个1秒触发一次的定时器 
    在MFC程序中SetTimer被封装在CWnd类中,调用就不用指定窗口句柄了,例如: 

    UINT   SetTimer(1,100,NULL); 
    函数反回值就是第一个参数值1,表示此定时器的ID号。 

    第二个参数表示要等待100毫秒时间再重新处理一次。第三个参数在这种方法中一般用NULL。 
    注意:设置第二个参数时要注意,如果设置的等待时间比处理时间短,程序就会出问题了。 

    1.2   调用回调函数 

    此方法首先写一个如下格式的回调函数 

    void   CALLBACK   TimerProc(HWND   hWnd,UINT   nMsg,UINT   nTimerid,DWORD   dwTime); 
    然后再用SetTimer(1,100,TimerProc)函数来建一个定时器,第三个参数就是回调函数地址。 

    2、多个定时器的实现与应用 


    我们在安装定时器时都为其指定了ID,使用多个定时器时,该ID就发挥作用了。 
    不使用MFC时,当接收到WM_TIMER消息,WPARAM   wParam中的值便是该定时器的ID 
    使用MFC时就更简单了,我们为其增加WM_TIME的消息处理函数OnTimer即可,请看如下例子 
    void   CTimerTestDlg::OnTimer(UINT   nIDEvent) 

    switch   (nIDEvent) 

    case   24:   ///处理ID为24的定时器 
    Draw1(); 
    break; 
    case   25:   ///处理ID为25的定时器 
    Draw2(); 
    break; 

    CDialog::OnTimer(nIDEvent); 

    当你用回调函数时,我们可以根据nTimerid的值来判断是哪个定时器,例如: 
    void   CALLBACK   TimerProc(HWND   hWnd,UINT   nMsg,UINT   nTimerid,DWORD   dwTime) 

    switch(nTimerid) 

    case   1:   ///处理ID为1的定时器 
    Do1(); 
    break; 
    case   2:   ///处理ID为2的定时器 
    Do2(); 
    break; 


    3、取消定时器 

    不再使用定时器后,我们应该调用KillTimer来取消定时,KillTimer的原型如下 

    BOOL   KillTimer( 
    HWND   hWnd,   //   窗口句柄 
    UINT_PTR   uIDEvent   //   ID 
    ); 
    在MFC程序中我们可以直接调用KillTimer(int   nIDEvent)来取消定时器。
    例子
    #include   <windows.h> 
    #include   <iostream> 
    VOID   CALLBACK   TimerProc(HWND   hwnd,UINT   uMsg,UINT   idEvent,DWORD   dwTime); 
    VOID   CALLBACK   TimerProc(HWND   hwnd,UINT   uMsg,UINT   idEvent,DWORD   dwTime) 

    std::cout   < <   "hello "   < <   std::endl; 


    void   main() 

    int   timer1   =   1; 
    HWND   hwndTimer;       
    MSG   msg;                     

    SetTimer(NULL,timer1,5000,TimerProc); 
    int   itemp; 
    while ( (itemp = GetMessage(&msg, NULL,NULL,NULL))&& (itemp!=0) &&  (-1 !=  itemp)) 
    {   
       if   (msg.message   ==   WM_TIMER)   
       {   
        std::cout   < <   "i   got   the   message "   < <   std::endl; 
        TranslateMessage(&msg);   
        DispatchMessage(&msg);     
        }   
    }   


    输出如下: 
    i   got   the   message 
    hello 
    i   got   the   message 
    hello 
    i   got   the   message 
    hello

    ---------------------------------------------------------------------------------------------------------------------------

    // timer.cpp : 定义控制台应用程序的入口点。
    //

    #include "stdafx.h"
    #include   <windows.h>  
    #include   <stdio.h>  
    #include   <conio.h>  

    unsigned   long   WINAPI   Thread(PVOID   pvoid);  
    void   main()  
    {  
        DWORD   dwThreadId;  
        printf("use   timer   in   workthread   of   console   application<masterz>\n");  
        HANDLE   hThread   =   CreateThread(    
            NULL,                                                 //   no   security   attributes    
            0,                                                       //   use   default   stack   size      
            Thread,                                     //   thread   function    
            0,                                 //   argument   to   thread   function    
            0,                                                       //   use   default   creation   flags    
            &dwThreadId);    
        DWORD   dwwait=WaitForSingleObject(hThread,1000*30);  
        switch(dwwait)  
        {  
        case   WAIT_ABANDONED:  
            printf("main   thread   WaitForSingleObject   return   WAIT_ABANDONED\n");  
            break;  
        case   WAIT_OBJECT_0:  
            printf("main   thread   WaitForSingleObject   return   WAIT_OBJECT_0\n");  
            break;  
        case   WAIT_TIMEOUT:  
            printf("main   thread   WaitForSingleObject   return   WAIT_TIMEOUT\n");  
            break;  
        }  
        CloseHandle(hThread);  
        _getch();  
    }  

    unsigned   long   WINAPI   Thread(PVOID   pvoid)  
    {  
        MSG   msg;  
        PeekMessage(&msg,   NULL,   WM_USER,   WM_USER,   PM_NOREMOVE);  
        UINT   timerid=SetTimer(NULL,111,3000,NULL);  
        BOOL   bRet;  
        int   count   =0;  
        while(   (bRet   =   GetMessage(   &msg,   NULL,   0,   0   ))   !=   0)  
        {    
            if   (bRet   ==   -1)  
            {  
                //   handle   the   error   and   possibly   exit  
            }  
            else  
                if(msg.message==WM_TIMER)  
                {  
                    count++;  
                    printf("WM_TIMER   in   work   thread   count=%d\n",count);  
                    if(count>4)  
                        break;  
                }  
                else  
                {  
                    TranslateMessage(&msg);    
                    DispatchMessage(&msg);    
                }  
        }  
        KillTimer(NULL,timerid);  
        printf("thread   end   here\n");  
        return   0;  
    }   
    展开全文
  • C++定时器实现

    2016-07-11 10:23:43
    C++定时器实现,简单易懂,高效
  • C++ 定时器 windows平台

    2021-01-10 18:57:35
    C++ 定时器 windows平台 资源 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录C++ 定时器 windows平台一、源代码二、前言三、实现过程1、思路2、编译运行环境2、定时任务信息3、定时...

    https://blog.csdn.net/qq_23530731/article/details/112434392


    一、源代码

    CSDN
    github
    如果有更新,会在github中同步更新,CSDN的下载不方便更新。

    二、前言

    C++里现有的定时器不常见。经常用的是mfc的OnTimer定时器,但这个定时器是和界面耦合的,在后台处理时会非常不方便。因此我自己封装了一个定时器。

    三、实现过程

    1、思路

    考虑如下:
    1.每个定时任务都有自己的信息,因此要定义一个结构体来记录定时任务参数。
    2.考虑使用一个单独的线程来实现定时逻辑,那么用一个线程去实现一个定时任务不太合理,因此一个定时器对象应该要可以处理多个定时任务。那么就要用一个列表来管理多个定时任务。同时,用一个无符号整数(ID)来标识定时任务。
    3.考虑节约资源问题,定时任务需要一个提前量,在还有几毫秒才能达到定时时间的任务,也可以执行。
    4.尽量符合设计模式的原则。设计定时任务接口、定时任务基类、定时任务子类,其中定时接口负责对外表现,定时任务基类负责定时任务逻辑,定时子类负责扩展。

    2、编译运行环境

    在windows平台下,C++语言,ISO C++14 标准,vs2019编译器。

    3、定时任务信息

    用一个结构体来描述定时任务信息,如下:

    	// 定时任务参数
    	struct STimerTask
    	{
    		std::function<void(void)> m_fun; // 定时执行函数
    		bool m_bLoop = false; // 是否循环执行 false只执行一次,true循环执行
    		unsigned int m_nInterval = 1000; // 定时时间间隔(毫秒)
    	};
    

    4、定时任务接口

    定时任务接口定义如下:

    	// 定时器接口
    	class CTimerInterface
    	{
    	public:
    		CTimerInterface() {};
    		virtual ~CTimerInterface() {};
    		// 添加定时任务
    		// nTimeId 定时Id,不能为0
    		// rTask 定时任务信息
    		// 返回定时任务,返回0时表示创建定时任务失败
    		virtual unsigned int AddTask(unsigned int nTimeId, const STimerTask& rTask) = 0;
    		// 清除所有定时任务
    		virtual void Clear() = 0;
    		// 移除一个定时任务
    		// nTimeId 要移除的定时任务的Id
    		virtual void Remove(unsigned int nTimeId) = 0;
    		// 设置定时的提前量,对于给定的任务,可以提前一定时间执行,单位毫秒
    		virtual void SetLeadTime(unsigned int nLeadTime)
    		{
    			m_nLeadTime = nLeadTime;
    		}
    	protected:
    		unsigned int m_nLeadTime = 20; // 执行任务的提前量,对于给定的任务可以提前m_nLeadTime毫秒执行, 说明:由于C++没有属性,因此使用一个变量来定义任务提前量了。	
    	};
    

    5、定时任务基类

    1)、头文件

    头文件代码如下:

    class CTimerBase :public CTimerInterface
    	{
    	public:
    		// strName 定时器名称
    		CTimerBase();
    		virtual ~CTimerBase();
    
    		// 添加定时任务
    		// nTimeId 定时Id,不能为0
    		// rTask 定时任务信息
    		// 返回定时任务,返回0时表示创建定时任务失败
    		virtual unsigned int AddTask(unsigned int nTimeId, const STimerTask& rTask);
    		// 清除所有定时任务
    		virtual void Clear();
    		// 移除一个定时任务
    		// nTimeId 要移除的定时任务的Id
    		virtual void Remove(unsigned int nTimeId);
    
    	protected:
    		// 启动定时
    		void Start();
    		// 停止定时
    		void Stop();
    
    		// 定时器工作线程执行函数
    		virtual void Work();
    		// 遍历定时列表,执行达到时间的任务
    		// 返回下一次遍历所需要的时间
    		virtual unsigned __int64 TraverseExecuteTasks();
    		// 执行定时任务
    		virtual void ExecuteTask(const STimerTask& rTask);
    
    		// 获取当前时间,不同的方式获取的时间,得到的定时精度不相同
    		virtual unsigned __int64 GetNowTime() = 0;
    
    	protected:
    		// 内部使用的定时任务
    		struct STimerTaskInter
    		{
    			STimerTask m_timerTask;
    			unsigned __int64 m_nStartTime = 0; // 用于计算时间间隔的开始时间
    		};
    	protected:
    		std::string m_strName; // 定时器名称
    
    		std::mutex m_timerLock; // 定时任务锁
    		std::mutex m_startStopWorkThreakLock; // 启停工作线程锁
    		std::atomic<bool> m_bRun = false; // 工作线程运行标志
    		std::thread* m_pWorkThreak = nullptr; // 工作线程
    		std::condition_variable_any m_workCondition; // 定时用条件变量,用其超时特性来定时,在定时的过程中也能随时唤醒
    		std::map<unsigned int,STimerTaskInter> m_mapTimerTask; // 定时任务集合
    	};
    };
    

    有很多备注,就简要说明几点:
    1.内部逻辑中,会用到开始定时的时间这个额外的参数,但这个参数是内部维护的不展示给调用者,因此额外定义了一个STimerTaskInter结构体。
    2.本来是想将定时器的运行线程设置上名称的,这样可以方便调试。但技术上遇到一些问题,无法完美,就没实现了。
    3.用一个map来保存多个任务,其中key为定时ID,值为定时任务参数信息。

    2)、实现

    定时任务基类的实现部分,AddTask、Clear、Remove是对map的维护,不说了。
    Start和Stop是对定时任务线程的启停控制,当没有定时任务时,线程不运行,当有任务后,要保证线程运行。同时Stop也要等待工作线程结束。略过。
    Work函数是处理定时任务逻辑线程的实现,其代码如下:

    void CTimerBase::Work()
    	{
    		unsigned __int64 nMinWaitTime = 0; // 所有任务中最小的等待时间,用于计算下一次执行需要多久
    		std::unique_lock<std::mutex> lks(m_timerLock);
    		while (m_bRun)
    		{
    			m_workCondition.wait_for(m_timerLock, std::chrono::milliseconds(nMinWaitTime));
    			if (!m_bRun)
    			{
    				return;
    			}
    			nMinWaitTime = TraverseExecuteTasks();
    			if (m_mapTimerTask.empty()) // 如果没有任务了,就退出
    			{
    				break;
    			}
    		}
    	}
    

    TraverseExecuteTasks函数的代码如下:

    	unsigned __int64 CTimerBase::TraverseExecuteTasks()
    	{
    		unsigned __int64 nMinWaitTime = 0xffffffffffffffff; // 所有任务中最小的等待时间,用于计算下一次执行需要多久
    		__int64 hadWaitTime = 0; // 一个任务已经等待了的时间
    		__int64 nNeedWaitTime = 0; // 一个任务还需要等待的时间
    
    		unsigned __int64 nNowTime = GetNowTime();
    		for (std::map<unsigned int, STimerTaskInter>::iterator it = m_mapTimerTask.begin(); it != m_mapTimerTask.end();)
    		{
    			STimerTaskInter& rTimerTaskInter = it->second;
    			hadWaitTime = nNowTime - rTimerTaskInter.m_nStartTime; // 已经等待了的时间
    			if (hadWaitTime < 0) // 不能小于0
    			{
    				hadWaitTime = 0;
    			}
    
    			if ((hadWaitTime + m_nLeadTime) >= rTimerTaskInter.m_timerTask.m_nInterval) // 时间间隔大于指定间隔时执行 可以提前m_nLeadTime毫秒执行
    			{
    				ExecuteTask(rTimerTaskInter.m_timerTask);
    				rTimerTaskInter.m_nStartTime = nNowTime;
    				if (!rTimerTaskInter.m_timerTask.m_bLoop) // 如果定时器只执行一次,则移除
    				{
    					it = m_mapTimerTask.erase(it);
    					continue;
    				}
    
    				nNeedWaitTime = rTimerTaskInter.m_timerTask.m_nInterval; // 一个任务还需要等待的时间
    			}
    			else
    			{
    				nNeedWaitTime = rTimerTaskInter.m_timerTask.m_nInterval - hadWaitTime; // 一个任务还需要等待的时间
    			}
    			assert(nNeedWaitTime > 0); // 按照逻辑,nNeedWaitTime必然大于0
    
    			// 找到最小的需要等待的时间
    			if ((unsigned __int64)nNeedWaitTime < nMinWaitTime)
    			{
    				nMinWaitTime = nNeedWaitTime;
    			}
    
    			++it;
    		}
    
    		assert(nMinWaitTime > 0); // nMinWaitTime必然大于0
    		return nMinWaitTime;
    	}
    

    work函数中有一个循环和TraverseExecuteTasks配合工作。每次先判断是否有定时任务达到时间了,达到就执行。然后,还要计算出最近的定时任务到达时间。最后再睡眠计算出的时间。以便程序睡眠,不用每一毫秒都去遍历。

    6、定时任务子类

    在windows平台下,我找到获取当前系统时间的方法有两个:
    QueryPerformanceCounter(…)
    GetTickCount64(…)
    其中,QueryPerformanceCounter精度高。GetTickCount64精度低,微软文档中说要隔十几毫秒才刷新一次值。在不同的条件下,需求不同的定时间隔。因此,在定时任务基类中,将获取系统时间功能定义为了纯虚函数,在子类中去实现不同的精度的定时。
    对两种精度,实现了两个子类类,CTickTimer和CHighPrecisionTimer。在使用时可以适当选用。这两个子类仅仅需要实现GetNowTime函数,逻辑简单,就略过了,请自行查看代码。

    7、调用

    在Main.cpp中,展示了定时任务的使用方法,其代码如下:

    #include <iostream>
    #include <sstream>
    #include <functional>
    #include "Timer/Head.h"
    
    // 定时测试函数
    void TimerTest(int n)
    {
    	std::cout << "TimerTest, n = " << n << std::endl;
    }
    
    
    // 定时测试类
    class CTimerTest
    {
    public:
    	// 成员变量作为定时执行的测试函数
    	void TimerTest(int n)
    	{
    		std::cout << "CTimerTest::TimerTest, n = " << n << std::endl;
    	}
    };
    
    int main()
    {
    	jaf::CHighPrecisionTimer timer; // 定义一个定时器
    
    	int n = 1; // 测试用参数
    
    	jaf::STimerTask task; // 定时任务
    	task.m_nInterval = 5000; // 定时5秒
    	task.m_bLoop = false; // 不循环定时
    	task.m_fun = std::bind(&TimerTest, n); // 用全局函数作为定时执行函数
    	timer.AddTask(1,task); // 添加第一个定时任务
    
    	task.m_nInterval = 1000; // 定时1秒
    	task.m_bLoop = true; // 循环定时
    	CTimerTest timerTest;
    	task.m_fun = std::bind(&CTimerTest::TimerTest, &timerTest, n); // 用类的成员函数作为定时执行函数
    	timer.AddTask(2,task); // 添加第2个定时任务
    
    
    	std::cout << "输入任意字符停止定时" << std::endl;
    	getchar();
    	timer.Remove(2);
    	//timer.Clear();
    
    	system("pause");
    	return 0;
    }
    

    注释很多,就略过了,请自行查阅。

    最后

    如果有什么问题、建议、读后感,可以留言讨论哟,我在有空的时候会回复。

    展开全文
  • 定时器的实现原理定时器的实现依赖的是CPU时钟中断,时钟中断的精度就决定定时器精度的极限。一个时钟中断源如何实现多个定时器呢?对于内核,简单来说就是用特定的数据结构管理众多的定时器,在时钟中断处理中判断...

    80370643597ac9289fa1840e4c73ea4c.png

    定时器的实现原理
    定时器的实现依赖的是CPU时钟中断,时钟中断的精度就决定定时器精度的极限。一个时钟中断源如何实现多个定时器呢?对于内核,简单来说就是用特定的数据结构管理众多的定时器,在时钟中断处理中判断哪些定时器超时,然后执行超时处理动作。而用户空间程序不直接感知CPU时钟中断,通过感知内核的信号、IO事件、调度,间接依赖时钟中断。用软件来实现动态定时器常用数据结构有:时间轮、最小堆和红黑树。下面就是一些知名的实现:

    Linux内核的 Hierarchy 时间轮算法
    Asio C++ Library最小堆定时器实现nginx 使用红黑树结构管理定时器事件
    Linux内核定时器相关的一些相关代码:内核启动注册时钟中断
    // @file: arch/x86/kernel/time.c - Linux 4.9.7// 内核init阶段注册时钟中断处理函数static struct irqaction irq0 = { .handler = timer_interrupt, .flags = IRQF_NOBALANCING | IRQF_IRQPOLL | IRQF_TIMER, .name = "timer"};void __init setup_default_timer_irq(void){ if (!nr_legacy_irqs()) return; setup_irq(0, &irq0);}// Default timer interrupt handler for PIT/HPETstatic irqreturn_t timer_interrupt(int irq, void *dev_id){ // 调用体系架构无关的时钟处理流程 global_clock_event->event_handler(global_clock_event); return IRQ_HANDLED;}内核时钟中断处理流程// @file: kernel/time/timer.c - Linux 4.9.7/* * Called from the timer interrupt handler to charge one tick to the current * process. user_tick is 1 if the tick is user time, 0 for system. */void update_process_times(int user_tick){ struct task_struct *p = current; /* Note: this timer irq context must be accounted for as well. */ account_process_tick(p, user_tick); run_local_timers(); rcu_check_callbacks(user_tick);#ifdef CONFIG_IRQ_WORK if (in_irq()) irq_work_tick();#endif scheduler_tick(); run_posix_cpu_timers(p);}/* * Called by the local, per-CPU timer interrupt on SMP. */void run_local_timers(void){ struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]); hrtimer_run_queues(); /* Raise the softirq only if required. */ if (time_before(jiffies, base->clk)) { if (!IS_ENABLED(CONFIG_NO_HZ_COMMON) || !base->nohz_active) return; /* CPU is awake, so check the deferrable base. */ base++; if (time_before(jiffies, base->clk)) return; } raise_softirq(TIMER_SOFTIRQ); // 标记一个软中断去处理所有到期的定时器}
    内核定时器时间轮算法
    单层时间轮算法的原理比较简单:用一个数组表示时间轮,每个时钟周期,时间轮 current 往后走一个格,并处理挂在这个格子的定时器链表,如果超时则进行超时动作处理,然后删除定时器,没有则剩余轮数减一。原理如图:
    d7438056443a5298fabb5f19da8924bb.png
    Linux 内核则采用的是 Hierarchy 时间轮算法,Hierarchy 时间轮将单一的 bucket 数组分成了几个不同的数组,每个数组表示不同的时间精度,Linux 内核中用 jiffies 记录时间,jiffies记录了系统启动以来经过了多少tick。下面是一些代码:
    // @file: kernel/time/timer.c - Linux 4.9.7/* * The timer wheel has LVL_DEPTH array levels. Each level provides an array of * LVL_SIZE buckets. Each level is driven by its own clock and therefor each * level has a different granularity. *//* Size of each clock level */#define LVL_BITS 6#define LVL_SIZE (1UL << LVL_BITS)/* Level depth */#if HZ > 100# define LVL_DEPTH 9# else# define LVL_DEPTH 8#endif#define WHEEL_SIZE (LVL_SIZE * LVL_DEPTH)struct timer_base { spinlock_t lock; struct timer_list *running_timer; unsigned long clk; unsigned long next_expiry; unsigned int cpu; bool migration_enabled; bool nohz_active; bool is_idle; DECLARE_BITMAP(pending_map, WHEEL_SIZE); struct hlist_head vectors[WHEEL_SIZE];} ____cacheline_aligned;
    Hierarchy 时间轮的原理大致如下,下面是一个时分秒的Hierarchy时间轮,不同于Linux内核的实现,但原理类似。对于时分秒三级时间轮,每个时间轮都维护一个cursor,新建一个timer时,要挂在合适的格子,剩余轮数以及时间都要记录,到期判断超时并调整位置。原理图大致如下:4132282ab60de0e476f4113399c66744.png
    定时器的使用方法

    在Linux 用户空间程序开发中,常用的定期器可以分为两类:

    执行一次的单次定时器 single-short;
    循环执行的周期定时器 Repeating Timer;
    其中,Repeating Timer 可以通过在Single-Shot Timer 终止之后,重新再注册到定时器系统里来实现。当一个进程需要使用大量定时器时,同样利用时间轮、最小堆或红黑树等结构来管理定时器。而时钟周期来源则需要借助系统调用,最终还是从时钟中断。Linux用户空间程序的定时器可用下面方法来实现:

    通过alarm()或setitimer()系统调用,非阻塞异步,配合SIGALRM信号处理;
    通过select()或nanosleep()系统调用,阻塞调用,往往需要新建一个线程;通过timefd()调用,基于文件描述符,可以被用于 select/poll 的应用场景;通过RTC机制, 利用系统硬件提供的Real Time Clock机制, 计时非常精确;
    上面方法没提sleep(),因为Linux中并没有系统调用sleep(),sleep()是在库函数中实现,是通过调用alarm()来设定报警时间,调用sigsuspend()将进程挂起在信号SIGALARM上,而且sleep()也只能精确到秒级上,精度不行。当使用阻塞调用作为定时周期来源时,可以单独启一个线程用来管理所有定时器,当定时器超时的时候,向业务线程发送定时器消息即可。
    一个基于时间轮的定时器简单实现
    #include #include #include #include #define TIME_WHEEL_SIZE 8typedef void (*func)(int data);struct timer_node { struct timer_node *next; int rotation; func proc; int data;};struct timer_wheel { struct timer_node *slot[TIME_WHEEL_SIZE]; int current;};struct timer_wheel timer = {{0}, 0};void tick(int signo){ // 使用二级指针删进行单链表的删除 struct timer_node **cur = &timer.slot[timer.current]; while (*cur) { struct timer_node *curr = *cur; if (curr->rotation > 0) { curr->rotation--; cur = &curr->next; } else { curr->proc(curr->data); *cur = curr->next; free(curr); } } timer.current = (timer.current + 1) % TIME_WHEEL_SIZE; alarm(1);}void add_timer(int len, func action){ int pos = (len + timer.current) % TIME_WHEEL_SIZE; struct timer_node *node = malloc(sizeof(struct timer_node)); // 插入到对应格子的链表头部即可, O(1)复杂度 node->next = timer.slot[pos]; timer.slot[pos] = node; node->rotation = len / TIME_WHEEL_SIZE; node->data = 0; node->proc = action;} // test case1: 1s循环定时器int g_sec = 0;void do_time1(int data){ printf("timer %s, %d\n", __FUNCTION__, g_sec++); add_timer(1, do_time1);}// test case2: 2s单次定时器void do_time2(int data){ printf("timer %s\n", __FUNCTION__);}// test case3: 9s循环定时器void do_time9(int data){ printf("timer %s\n", __FUNCTION__); add_timer(9, do_time9);}int main(){ signal(SIGALRM, tick); alarm(1); // 1s的周期心跳 // test add_timer(1, do_time1); add_timer(2, do_time2); add_timer(9, do_time9); while(1) pause(); return 0;}
    在实际项目中,一个常用的做法是新起一个线程,专门管理定时器,定时来源使用rtc、select等比较精确的来源,定时器超时后向主要的work线程发消息即可,或者使用timefd接口。

    -END-

    推荐阅读

    【01】现在市场上,C++ 主要用来做什么?【02】用C语言编程,如何节省存储空间?【03】处理器知识:RISC和CISC架构分析对比【04】码了一年才懂:C++深入理解浅拷贝和深拷贝【05】那个神奇的555定时器,你知道它的功能有多强大吗?免责声明:整理文章为传播相关技术,版权归原作者所有,如有侵权,请联系删除d5f82d22b91a52bb7fc27ffc05cf4c57.png
    展开全文
  • 主要给大家介绍了关于C++定时器Timer在项目中的基本使用方法,文中通过示例代码介绍的非常详细,对大家学习或者使用C++具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
  • C++定时器CTimer的实现

    万次阅读 热门讨论 2018-10-26 17:01:02
    个人在使用C++11进行项目开发时,想找一些比较完善、系统化的基本功能库却很少,比如定时器。 这里,主要利用thread、mutex、condition_variable_any、function来实现定时器,可现实同步、异步、单次、循序执行任务...
  • 定时器_c++定时器管理

    2021-01-12 05:56:16
    在上篇中提了定时器的两种实现的思路,同时也提到了一个问题,就是当一个进程需要使用大量定时器时,需要利用时间轮、最小堆或红黑树等结构来管理定时器。为什么要这样呢?我们先回顾下上篇的观点,上篇中提到定时器...
  • c++ 定时器使用

    2011-08-30 15:04:13
    c++ 定时器使用  1.1 用WM_TIMER来设置定时器 先请看SetTimer这个API函数的原型 UINT_PTR SetTimer( HWND hWnd, // 窗口句柄 UINT_PTR nIDEvent,
  • C++定时器SetTimer实际ID说明

    千次阅读 2017-09-24 16:09:22
    C++定时器SetTimer 函数原型: UINT_PTR SetTimer(  HWND  hWnd, // 窗口句柄  UINT_PTR nIDEvent,  // 定时器ID,多个定时器时,可以通过该ID判断是哪个定时器  UINT   uElapse, // ...
  • 今天,我们来讲下C++定时器的实现。个人认为一个完备的定时器需要有如下功能:在某一时间点执行某一任务在某段时间后执行某一任务重复执行某一任务N次,任务间隔时间T那么如何实现定时器呢?下面是我自己实现的...
  • 个人简单设计的定时器与线程的使用,简单明了,适用于C++初学者
  • C++定时器功能实现

    万次阅读 2019-06-20 21:15:28
    最近编写代码过程中,需要用到一个定时器的功能,我们知道c++只提供了一个头文件#include <ctime>,并没有提供定时器功能函数,所以需要自己编写一个定时器功能的函数,Qt中可以通过信号与槽机制实现定时器功能...
  • 高性能服务器开发之C++定时器 来源:https://www.cnblogs.com/junye/p/5836552.html 写这篇文章前搜了下网上类似的文章,有很多,所以笔者的这篇文章就不对定时器的常见实现方法加以说明,也不进行性能比较,直接上...
  • 555定时器:555定时器简化原理图如图所示; 它由3个阻值为5kW的电阻组成的分压器、两个电压比较器C1和C2、基本RS触发器、放电晶体管T以及缓冲器G组成; RD为复位输入端, 当RD为低电平时, 不管其它输入端的状态如何,...
  • C++定时器代码

    热门讨论 2011-09-28 15:27:00
    一个简单的定时器代码,可以进简单的计时效果。
  • c++定时器实现

    千次阅读 2017-03-02 13:38:56
    随便写了一个简单定时器 #pragma once #include #include #include using namespace std; struct tTIMER { unsigned nID; //定时器ID unsigned nTimeElapce; //定时器运行间隔(ms) unsigned nTimeCreate; ...
  • 一、背景介绍笔者最近在使用Go的定时器,发现Go提供的time包里面,按照下面几种场景做了区分,并分别提供一些API支持。主要场景如下所示:1.超时一次之后,就不再使用的定时器,time.After()。2.每隔一段时间,就...
  • 小蜜蜂老师:https://www.bilibili.com/video/BV1m7411H7oT?p=5参考连接:...TIM1和TIM8是高级定时器TIM2-TIM5是通用定时器TIM6和TIM7是基本的定时器这8个定时器都是16位的,它们的计数器的类型...
  • C++定时器的实现

    2020-05-25 22:19:04
    个人认为一个完备的定时器需要有如下功能: 在某一时间点执行某一任务 在某段时间后执行某一任务 重复执行某一任务N次,任务间隔时间T 如何实现定时器 下面是我自己实现的定时器逻辑,源码链接最后会附上。 ...
  • c++ 定时器SetTimer

    万次阅读 2018-06-12 14:31:45
    本文列举两个例子进行说明。例 1, 不使用回调函数,是按照时间到后 去响应一个自定义的函数#include&lt;iostream&gt; #include&lt;Windows.h&gt; using namespace std;... DOWRD dwTimerId...
  • 1. 定时器的原理2. 定时器的使用CPU时序的有关知识振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期)状态周期:2个振荡周期为1个状态周期,用S表示。振荡周期又称S周期或时钟周期。机器周期:1...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,460
精华内容 584
关键字:

c++定时器

c++ 订阅