歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Linux下的多線程定時器實現

一、功能:

Linux下編寫一個程序庫,實現定時器的功能,它能為用戶提供在同一進程中多次使用的定時器。

二、實現

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <pthread.h>
#include <errno.h>
#define DEFAULT_INTERVAL 1


int TIMER_CNT = 0;
class Timer;                //定時器
class TimerManager;        //定時器管理器


class TimerManager {

    friend class Timer;    //友元類,Timer類可分享此類的方法
public:
    typedef enum {

        TIMER_MANAGER_STOP=0,
        TIMER_MANAGER_START
    }TimerManagerState;
    static TimerManager *instance();    //當前實例
    void start();                      //啟動當前線程,運行process函數
    void stop();                        //終止當前線程
    void dump();                        //清理當前剩下的無用定時器
    void add_timer(Timer *vtimer);      //線程安全的增加定時器
    void remove_timer(Timer *vtimer);  //線程安全的移除定時器
protected:
    static void *process(void *);

private:
    TimerManager();
    void add_timer_unsafe(Timer *vtimer);  //線程非安全的增加定時器,本類使用
    void remove_timer_unsafe(Timer *vtimer);//線程非安全的移除定時器,本類使用

    static TimerManager *m_instance;
    static pthread_mutex_t workmutex;
    TimerManagerState m_state;
    LIST_HEAD(,Timer) list_;                //鏈表頭
    static int mark;
};
class Timer {

    friend class TimerManager;
public:
    typedef enum {

        TIMER_IDLE = 0,
        TIMER_ALIVE,
        TIMER_TIMEOUT
    }TimerState;
    Timer(int vinterval,void (*vfunc)(void *),void *vdata);
    void start();                          //把自己添加進定時器管理器的鏈表裡
    void stop();                            //把自己從定時器管理器的鏈表裡移除
    void reset(int vinterval);              //重置
    ~Timer();

private:
    int id;                                //當前定時器的ID
    int m_interval;                        //定時器的定時時間,單位為秒
    int m_counter;                          //還剩下多少時間,單位為微秒
    TimerState m_state;                    //當前定時器的狀態
    void (*m_func)(void *);
    void *m_data;
    LIST_ENTRY(Timer) entry_;              //當前定時器在鏈表中的地址
};


TimerManager *TimerManager::m_instance;
pthread_mutex_t TimerManager::workmutex;


TimerManager::TimerManager() {

    pthread_mutex_init(&workmutex,NULL);
}
TimerManager *TimerManager::instance() {

    if (m_instance == NULL) {

        pthread_mutex_lock(&workmutex);
        if (m_instance == NULL)
            m_instance = new TimerManager();
        pthread_mutex_unlock(&workmutex);
    }
    return m_instance;
}
void TimerManager::start() {

    if (m_state == TIMER_MANAGER_STOP) {

        m_state = TIMER_MANAGER_START;
        pthread_t pid;
        int res = pthread_create(&pid,NULL,process,this);
        if (res != 0) {

            printf("pthread_create is failed\n");
            exit(EXIT_FAILURE);
        }
    }
}
void TimerManager::stop() {

    this->m_state = TIMER_MANAGER_STOP;
}
void TimerManager::dump() {

    Timer *item;
    pthread_mutex_lock(&workmutex);
    LIST_FOREACH(item,&(this->list_),entry_) {

        if (item->m_counter == 0) {

            printf("Timer%d will be dumped!\n",(int)item->m_data);
            item->stop();
        }
    }
    pthread_mutex_unlock(&workmutex);
}
void *TimerManager::process(void *arg) {

    pthread_detach(pthread_self());


    TimerManager *manage = (TimerManager *)arg;
    Timer *item;
    struct timeval start,end;
    int delay;
    struct timeval tm;
    gettimeofday(&end,0);


    while (manage->m_state == TIMER_MANAGER_START) {

        tm.tv_sec = 0;
        tm.tv_usec = DEFAULT_INTERVAL * 1000;
        start.tv_sec = end.tv_sec;
        start.tv_usec = end.tv_usec;
        while (select(0,0,0,0,&tm) < 0 && errno == EINTR);
        gettimeofday(&end,0);


        delay = (end.tv_sec - start.tv_sec) * 1000;
        delay += (end.tv_usec - start.tv_usec);
        pthread_mutex_lock(&manage->workmutex);
        LIST_FOREACH(item, &(manage->list_), entry_) {

            //printf("m_data = %d m_counter = %d ",item->m_data,item->m_counter);
            if ( item->m_counter < delay)
                item->m_counter = 0;
            else
                item->m_counter -= delay;
            //printf("m_data = %d m_counter = %d\n",item->m_data,item->m_counter);


            if (item->m_counter == 0) {

                if (item->m_func)
                    item->m_func(item->m_data);
                manage->remove_timer_unsafe(item);
                item->m_state = Timer::TIMER_TIMEOUT;
            }
        }
        pthread_mutex_unlock(&manage->workmutex);
    }
}
void TimerManager::add_timer(Timer* vtimer) {

    pthread_mutex_lock(&workmutex);
    LIST_INSERT_HEAD(&(this->list_),vtimer,entry_);
    pthread_mutex_unlock(&workmutex);
}
void TimerManager::remove_timer(Timer* vtimer) {

    pthread_mutex_lock(&workmutex);
    LIST_REMOVE(vtimer, entry_);
    pthread_mutex_unlock(&workmutex);
}
void TimerManager::add_timer_unsafe(Timer* vtimer) {

    LIST_INSERT_HEAD(&(this->list_),vtimer,entry_);
}
void TimerManager::remove_timer_unsafe(Timer* vtimer) {

    LIST_REMOVE(vtimer, entry_);
}

還有一個類的代碼先不上傳,這是我們正在學的Linux一個實驗,上傳的話萬人都交這個代碼給老師了。

三、總結:

1、按照之前我寫代碼的習慣,都是先聲明一個類A,,接著依次實現A的方法,然後再聲明一個類B,接著依次實現B的方法。

按照這個習慣,如果類A在類B的前面聲明,如果類A要調用類B中的方法,就必須在定義之前聲明class A,class B,接著把類A聲明為類B的友元類,還要把實現放在兩個類定義的後面,才不會出錯。

按照這個習慣,如果類A要調用類B的方法,類B要調用A的方法,這個要怎麼辦?到底哪個類放在前面?和上面的情況相似。我們一定要保證某個類在調用另一個類的對象,另一個類的方法都已經實現。否則會出現類似invalid use of incomplete type ‘struct Timer’這樣的錯誤。

2、當類中聲明了靜態變量,而且未初始化。這時候要在類外單獨再聲明一次靜態變量,這樣系統會自動給這些變量分配全局內存空間。這樣,後面就可以放心地對靜態變量進行賦值了。不然會出現類似於undefined reference to `TimerManager::m_instance'這樣的錯誤。

3、程序中出現了死鎖,process中用lock和unlock鎖起一段代碼,而這段代碼中又有用lock和unlock鎖起一段斷碼,這樣後一段代碼無法執行切程序處於掛起等待狀態。

4、我的實現是通過select不斷的檢查時間,可是select這個函數調用占用了一些時間導致精度誤差。如果我盡量少調用select函數,那麼實時性不夠,如果調用次數很多,那麼精確度又不夠。在我的代碼中折中,選取1s作為調用select的間隔。

5、當Timermanager類調用自身的add_timer_於remove_timer_時可能會發生一些錯誤,因為沒有加鎖,但是加鎖了就馬上死鎖,這個問題尚待解決。

Copyright © Linux教程網 All Rights Reserved