(筆記)Linux延時及時間函數總結


 

一、 基礎知識
1、時間類型。Linux下常用的時間類型有4個:time_t,struct timeval,struct timespec,struct tm。
(1)time_t是一個長整型,一般用來表示用1970年以來的秒數。
(2)Struct timeval有兩個成員,一個是秒,一個是微妙。

linux 延時函數幾個資料 - mervyn807 - mervyn807的博客struct timeval {
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客               long tv_sec;              /* seconds */
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客               long tv_usec;            /* microseconds */
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客       };

(3)struct timespec有兩個成員,一個是秒,一個是納秒。

linux 延時函數幾個資料 - mervyn807 - mervyn807的博客struct timespec{
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                time_t  tv_sec;         /* seconds */
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                long    tv_nsec;        /* nanoseconds */
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        };

(4)struct tm是直觀意義上的時間表示方法:

linux 延時函數幾個資料 - mervyn807 - mervyn807的博客struct tm {
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                      int     tm_sec;          /* seconds */
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                      int     tm_min;          /* minutes */
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                      int     tm_hour;        /* hours */
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                      int     tm_mday;       /* day of the month */
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                      int     tm_mon;         /* month */
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                      int     tm_year;         /* year */
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                      int     tm_wday;        /* day of the week */
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                      int     tm_yday;         /* day in the year */
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                      int     tm_isdst;         /* daylight saving time */
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客              };

2、 時間操作
(1) 時間格式間的轉換函數
主要是 time_t、struct tm、時間的字符串格式之間的轉換。看下面的函數參數類型以及返回值類型:

linux 延時函數幾個資料 - mervyn807 - mervyn807的博客char *asctime(const struct tm *tm);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客char *ctime(const time_t *timep);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客struct tm *gmtime(const time_t *timep);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客struct tm *localtime(const time_t *timep);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客time_t mktime(struct tm *tm);

gmtime和localtime的參數以及返回值類型相同,區別是前者返回的格林威治標准時間,后者是當地時間。
(2) 獲取時間函數
兩個函數,獲取的時間類型看原型就知道了:

linux 延時函數幾個資料 - mervyn807 - mervyn807的博客time_t time(time_t *t);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客int gettimeofday(struct timeval *tv, struct timezone *tz);

前者獲取time_t類型,后者獲取struct timeval類型,因為類型的緣故,前者只能精確到秒,后者可以精確到微秒。

 


二、 延遲函數
主要的延遲函數有:sleep(),usleep(),nanosleep(),select(),pselect().

linux 延時函數幾個資料 - mervyn807 - mervyn807的博客unsigned int sleep(unsigned int seconds);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客void usleep(unsigned long usec);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客int nanosleep(const struct timespec *req, struct timespec *rem);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客int pselect(int   n,   fd_set   *readfds,  fd_set  *writefds,  fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);

alarm函數是信號方式的延遲,這種方式不直觀,這里不說了。
僅通過函數原型中時間參數類型,可以猜測sleep可以精確到秒級,usleep/select可以精確到微妙級,nanosleep和pselect可以精確到納秒級。
而實際實現中,linux上的nanosleep和alarm相同,都是基於內核時鍾機制實現,受linux內核時鍾實現的影響,並不能達到納秒級的精度,man nanosleep也可以看到這個說明,man里給出的精度是:Linux/i386上是10 ms ,Linux/Alpha上是1ms。
這里有一篇文章http://blog.csdn.net/zhoujunyi/archive/2007/03/30/1546330.aspx,測試了不同延遲函數之間的精確度。文章給出的結論是linux上精度最高的是select,10ms級別。我在本機器測試select和pselect相當,都達到了1ms級的精度,精度高於文章中給出的10ms,sleep在秒級以上和usleep/nanosleep相當。下面貼下我機器上1ms時候的測試結果,其他不貼了:

linux 延時函數幾個資料 - mervyn807 - mervyn807的博客sleep                      1000          0           -1000 
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客usleep                    1000       2974         1974 
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客nanosleep              1000       2990         1990 
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客select                      1000        991         -9 
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客pselect                    1000        990         -10 
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客gettimeofday           1000       1000          0

而使用gettimeofday循環不停檢測時間,可精確微秒級,不過不適宜用來做定時器模塊。
因此后面的定時期模塊將選擇select為延遲函數。

 


三、 定時器模塊需求以及實現概述
1、需求。從實現結果的角度說來,需求就是最終的使用方式。呵呵,不詳細描述需求了,先直接給出我實現的CTimer類的三個主要接口:

linux 延時函數幾個資料 - mervyn807 - mervyn807的博客Class CTimer{
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客Public:
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客CTimer(unsigned int vinterval,void (*vfunc)(CTimer *,void *),void *vdata,TimerType vtype);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客void start();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客void stop();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客void reset(unsigned int vinterval);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客};

使用定時器模塊的步驟如下:
(1) 實例化一個CTimer,參數的含義依次是:vinterval間隔時間(單位ms),vfunc是時間到回調的函數,vdata回調函數使用的參數,vtype定時器的類型,分一次型和循環型兩種。
(2) 調用start方法。
(3) 必要的時候調用stop和reset。
2、實現。簡單描述下定時器模塊的實現,有一個manager單例類保存所有CTimer對象,開啟一線程運行延遲函數,每次延遲間隔到,掃描保存CTimer的容器,對每個CTimer對象執行減少時間操作,減少到0則執行回調函數。對一次性CTimer,超時則從容器中刪除,循環型的將間隔時間重置,不從容器中移除。
CTimer的start執行將對象插入到manager容器中操作;stop執行將對象從manager容器中刪除的操作;reset執行先刪除,重置間隔,然后再放到容器中,reset不改變CTimer的定時器類型屬性。

 


四、 定時器模塊的數據結構選擇
Manager類的容器要頻繁進行的操作涉及插入、刪除、查詢等。
誤區:(1)簡單看,好象該容器要是有序的,方便插入刪除等,貌似紅黑樹比較合適。其實不然,插入刪除操作的頻率很低,最頻繁的還是每次時延到,對容器的掃描並做時間減少操作,紅黑樹在做順序掃描相對鏈表並沒什么優勢。
(2) 插入的時候依照順序鏈表的方式插入到合適的位置保持排序,以保證超時的對象都在鏈表的頭端。其實這也是沒必要的,每次時延到,對每一個對象都要做時間減少操作,因此不管是有序還是無序,都是一次掃描就執行完下面操作:減少時間、判斷是否超時,是則執行回調,繼續判斷是什么類型,一次型的則執行完就移除,循環型則執行完直接重置間隔就可。
因此,只需要能快速插入頭、刪除結點、遍歷就好。我的實現直接使用BSD內核中的數據結構LIST,插入頭、刪除時間復雜度都是1,遍歷就不說了。linux下/usr/include/sys下有頭文件queue.h里也有LIST結構以及操作的定義。貌似linux下的少了遍歷宏:

linux 延時函數幾個資料 - mervyn807 - mervyn807的博客#define LIST_FOREACH(var, head, field)     \
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客 for((var) = LIST_FIRST(head);     \
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客     (var)!= LIST_END(head);     \
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客     (var) = LIST_NEXT(var, field))
 
 

五、 詳細實現
這里帖出主要的代碼,請重點關注CTimerManager:: process方法,不再詳細說了。需要詳細的全部代碼,可來信索取,整體代碼很簡單,就兩個類。

linux 延時函數幾個資料 - mervyn807 - mervyn807的博客class CTimer
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客{
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客friend class CTimerManager;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客public:
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    typedef enum
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    {
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        TIMER_IDLE=0,   //start前以及手動調用stop后的狀態
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        TIMER_ALIVE,      //在manager的list里時候的狀態
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        TIMER_TIMEOUT  //超時后被移除的狀態,循環型的沒有
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    }TimerState;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    typedef enum
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    {
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        TIMER_ONCE=0,  //一次型
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        TIMER_CIRCLE     //循環型
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    }TimerType;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimer(unsigned int vinterval,void (*vfunc)(CTimer *,void *),void *vdata,TimerType vtype);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    void start();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    void stop();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    void reset(unsigned int vinterval);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    ~CTimer();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客private:
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    unsigned int id_;              //測試用
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    unsigned int m_interval;  //間隔,不變
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    unsigned int m_counter;  //開始設置為interval,隨延遲時間到,減少
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    TimerState m_state;        //狀態
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    TimerType m_type;          //類型
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    void (*m_func)(CTimer *,void *);//回調函數
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    void * m_data;  //回調函數參數
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    LIST_ENTRY(CTimer) entry_;  //LIST的使用方式
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客};
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客/*構造函數*/
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客CTimer::CTimer(unsigned int vinterval,void (*vfunc)(CTimer *,void *),void *vdata,TimerType vtype):
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    m_interval(vinterval),m_counter(vinterval),
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    m_state(TIMER_IDLE),m_type(vtype),
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    m_func(vfunc),m_data(vdata)
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客{}
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客/*開始定時器*/
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客void CTimer::start()
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客{
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimerManager::instance()->add_timer(this);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客}
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客/*停止定時器*/
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客void CTimer::stop()
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客{
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimerManager::instance()->remove_timer(this);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客}
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客/*reset定時器*/
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客void CTimer::reset(unsigned int vinterval)
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客{
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimerManager::instance()->remove_timer(this);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    m_counter=m_interval=vinterval;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimerManager::instance()->add_timer(this);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客}
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客/*析構函數,stop操作不能省略,避免delete前忘記stop*/
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客CTimer::~CTimer()
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客{
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    if(m_state==TIMER_ALIVE)
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        stop();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客}

CTimerManager的:

linux 延時函數幾個資料 - mervyn807 - mervyn807的博客class CTimerManager
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客{
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客public:
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    typedef enum
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    {
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        TIMER_MANAGER_STOP=0,
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        TIMER_MANAGER_START
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    }TimerManagerState;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    static CTimerManager * instance();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    void add_timer(CTimer * vtimer);//線程安全的add
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    void remove_timer(CTimer * vtimer);//線程安全的remove
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    void start();  //開始process線程
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    void stop();  //停止process線程
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    void dump();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客protected:
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    static void * process(void *);             //實際的定時器時間延遲線程
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客private:    
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    void add_timer_(CTimer * vtimer);      //非線程安全的add
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    void remove_timer_(CTimer * vtimer);//非線程安全的remove
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimerManager();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    static pthread_mutex_t m_mutex;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    static CTimerManager * m_instance;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    TimerManagerState m_state;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    LIST_HEAD(,CTimer) list_;  //LIST使用方式
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    static unsigned int mark;  //測試,配合dump
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客};
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客/*singlton的double-check實現*/
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客CTimerManager * CTimerManager::instance()
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客{
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    if(m_instance==NULL)
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    {
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        pthread_mutex_lock(&m_mutex);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        if(m_instance==NULL)
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        {
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客            m_instance=new CTimerManager();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        }
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        pthread_mutex_unlock(&m_mutex);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    }
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    return m_instance;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客}
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客/*process必須static,不能操作非static屬性,因此傳遞this指針*/
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客void CTimerManager:: start()
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客{
         if(m_state==TIMER_MANAGER_STOP){
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    m_state=TIMER_MANAGER_START;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    pthread_t pid;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    pthread_create(&pid,0,process,this);
      }
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客}
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客/*定時器模塊延遲時間線程*/
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客void * CTimerManager:: process(void * arg)
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客{
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    pthread_detach(pthread_self());
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimerManager *manage=(CTimerManager *)arg;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimer *item;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    struct timeval start,end;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    unsigned int delay;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    struct timeval tm;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    gettimeofday(&end,0);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客/*使用狀態控制線程運行,進而容易實現stop,也可以使用pthread_cancel粗暴的停止,需要考慮暫停點等問題*/
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    while(manage->m_state==TIMER_MANAGER_START)
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    {
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        tm.tv_sec=0;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        tm.tv_usec=DEFULT_INTERVAL*1000;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        start.tv_sec=end.tv_sec;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        start.tv_usec=end.tv_usec;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客/*不同系統的延遲函數精度不同,如果需要替換為其他延遲函數,這附近修改下就好*/
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        while(select(0,0,0,0,&tm)<0&&errno==EINTR);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        gettimeofday(&end,0);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        delay=(end.tv_sec-start.tv_sec)*1000+(end.tv_usec-start.tv_usec)/1000;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        pthread_mutex_lock(&manage->m_mutex);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        LIST_FOREACH(item, &(manage->list_), entry_)
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        {
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客            item->m_counter<delay?item->m_counter=0:item->m_counter-=delay;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客            if(item->m_counter==0)
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客            {
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                if(item->m_func)
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                item->m_func(item,item->m_data);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                if(item->m_type==CTimer::TIMER_ONCE)
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                {
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客/*一次型的,超時,移除,並狀態CTimer::TIMER_TIMEOUT*/
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                    manage->remove_timer_(item);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                    item->m_state=CTimer::TIMER_TIMEOUT;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                }
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                else if(item->m_type==CTimer::TIMER_CIRCLE)
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                {
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客/*循環型的,重置counter就好*/
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                    item->m_counter=item->m_interval;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客                }
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客            }
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        }
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客        pthread_mutex_unlock(&manage->m_mutex);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    }
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客}



六、 討論
(1)精度問題。精度高,實時性高,但要求select等待的時間縮短,進而增加對LIST結構的掃描操作。精度低,實時性差,但會增加定時器線程的睡眠時間,減少對cpu的占用。一般的應用系統,應該盡量降低精度,避免不必要的掃描,對具體系統可考察所用到的所有定時器的實際間隔,在允許的情況下,盡量降低精度,可通過修改代碼中的宏實現。為了降低定時器線程對cpu的占有時間,甚有更為粗獷型的定時器模塊實現為將延遲時間取list中最小的那個間隔,保證每次延遲時間到都有回調。
(2)加鎖區域問題。本文中的定時器模塊實現,將定時器對象的時間減少以及函數回調的執行等再同一個臨界區內執行,而有的定時器模塊實現是在加鎖區域執行“時間減少”操作,將減少到0的對象放到另一個超時鏈表中,解鎖后再單獨掃描超時鏈表執行回調操作。很明顯,后者縮短了加鎖時間,能及時響應其他的線程的定時器對象的start以及stop操作。但是后者對定時器操作的時序性有誤差,直觀反應就是可能在定時器執行了stop操作以后,仍然會有超時回調發生,特別是回調參數是指針的情況,可能引起難以發現的bug,增加調試困難。在衡量兩者的利弊后,本文采用延長加鎖時間以保證操作的時序性。因此,在實際的使用,回調函數應盡快返回,另一方面,盡量減少系統內使用的定時器數目,這個主要原因是延遲時間到要掃描LIST,哪種方式都避免不了。

 

 

七、使用示例

linux 延時函數幾個資料 - mervyn807 - mervyn807的博客#include "timer_manager.h"
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客#include <stdio.h>
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客#include <unistd.h>
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客#include <stdlib.h>
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客void func(CTimer * timer, void *data)
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客{
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    printf("hi,%d\n",(int)(data));
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客}
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客/*隨便寫的,湊合着看吧。沒有CTimerManager::instance()->stop();也沒new對象。定時器對象可多次start和stop,使用上對暴露的接口沒有任何的契約式限制,可隨意調用*/
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客int main()
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客{
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimerManager::instance()->start();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimer a(1000,func,(void *)1,CTimer::TIMER_CIRCLE);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimer a1(2000,func,(void *)11,CTimer::TIMER_ONCE);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimer a2(3000,func,(void *)12,CTimer::TIMER_ONCE);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimer a3(1000,func,(void *)13,CTimer::TIMER_ONCE);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    a.start();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    a1.start();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    a2.start();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    a3.start();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    a.start();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    a1.start();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    a2.start();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    a3.start();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    sleep(1);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimerManager::instance()->dump();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    sleep(1);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    CTimerManager::instance()->dump();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    a.reset(2000);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    a1.stop();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    a3.stop();
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    sleep(10);
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客    return 0;
linux 延時函數幾個資料 - mervyn807 - mervyn807的博客}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM