一、功能:
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_時可能會發生一些錯誤,因為沒有加鎖,但是加鎖了就馬上死鎖,這個問題尚待解決。