Linux下定時器的使用

Linux下應用層定時器原本有好幾種,大夥能夠去搜索其餘帖子博客,這裏我主要描述我在使用setitimer時候遇到的問題,話很少說,直接上代碼吧
linux

例子一:只有定時器任務,爲模擬複雜,我特地加個鎖操做

// lock_timmer_test.cpp
ios

#include <iostream>
#include <sys/time.h>
#include <signal.h>
#include <linux/types.h>
#include <sched.h>
#include <pthread.h> 
using namespace std;

//互斥鎖
class MutexLock
{
public:
    MutexLock(){ pthread_mutex_init(&m_stMutex, NULL); }
    ~MutexLock(){ pthread_mutex_destroy(&m_stMutex); }

    void lock(){  pthread_mutex_lock(&m_stMutex);}
    int unlock() {return pthread_mutex_unlock(&m_stMutex); }

    bool trylock(){ return pthread_mutex_trylock(&m_stMutex) == 0;}
    pthread_mutex_t* getMutexPtr(){ return &m_stMutex;}
private:
    pthread_mutex_t m_stMutex;
};


MutexLock test_mutex;
int timestep = 1;  //定時器的時間間隔

//五秒切換插入map順序
void timeout_cb(int sig)
{
    test_mutex.lock();
    std::cout<<"  timer lock : "<<std::endl;
    test_mutex.unlock();
}


//開啓定時器
void set_timer() 
{   
    struct sigaction sigact;
    sigact.sa_flags =  0;
    sigact.sa_handler = timeout_cb; // 設置定時器的回調函數
    sigemptyset(&sigact.sa_mask);
    sigaction(SIGALRM, &sigact, NULL);

    // //結構成員it_value指定首次定時的時間,
    // // 結構成員it_interval指定下次定時的時間
    // // 定時器工做時,先將it_value的時間值減到0,
    // // 發送一個信號,再將it_value賦值爲it_interval的值,從新開始定時,如此反覆。
    struct itimerval itv;
    itv.it_interval.tv_sec = timestep;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = timestep;
    itv.it_value.tv_usec = 0;
    setitimer(ITIMER_REAL, &itv, NULL);
}


int main(int argc, char const *argv[])
{
    set_timer();
    while(true)
    {
        // test_mutex.lock();
        // std::cout<<"               main lock"<<std::endl;
        // test_mutex.unlock();
    }

    return 0;
}

編譯:g++ lock_timmer_test.cpp      運行: ./a.out函數

運行結果證實:的確每隔timestep,回調函數就會被調用,執行特定任務學習


舉例二:問題引伸一下,咱們把上述代碼中main函數中的註釋去掉,這時候會發生奇怪現象,整個程序會在回調調用的以後死鎖,如圖所示:

    回調函數作的操做複雜後,可能因爲調度問題或者是我本身代碼沒寫好仍是其餘緣由,在本例中,會出現死鎖問題,查了相應的資料,有人建議回調函數不要太複雜,可能因爲CPU調度問題和定時事件過短的而形成不肯定因素。
ui

    因此在使用setitimer()的時候,咱們儘可能不要讓回調函數作過於複雜的操做,最好只是作某些標誌位的置位操做就好。spa

詳情參見例四。
線程

舉例三:使用setitimer()定時器的時候,貌似不支持多個定時器的設置,詳情看代碼及運行結果,具體緣由是什麼,還請各位知道的指教指教。

#include <iostream>
#include <sys/time.h>
#include <signal.h>
#include <linux/types.h>
#include <sched.h>
#include <pthread.h> 

using namespace std;

//互斥鎖
class MutexLock
{
public:
    MutexLock(){ pthread_mutex_init(&m_stMutex, NULL); }
    ~MutexLock(){ pthread_mutex_destroy(&m_stMutex); }

    void lock(){  pthread_mutex_lock(&m_stMutex);}
    int unlock() {return pthread_mutex_unlock(&m_stMutex); }

    bool trylock(){ return pthread_mutex_trylock(&m_stMutex) == 0;}
    pthread_mutex_t* getMutexPtr(){ return &m_stMutex;}
private:
    pthread_mutex_t m_stMutex;
};


MutexLock test_mutex;
int timestep = 1;

//五秒切換插入map順序
void timeout_cb(int sig)
{
    test_mutex.lock();
    std::cout<<"  timer lock : "<<std::endl;
    test_mutex.unlock();
}
//五秒切換插入map順序
void timeout_cb2(int sig)
{
    test_mutex.lock();
    std::cout<<"  timer2 lock : "<<std::endl;
    test_mutex.unlock();
}

void set_timer() 
{   
    struct sigaction sigact;
    sigact.sa_flags =  0;
    sigact.sa_handler = timeout_cb; //定時器回調函數
    sigemptyset(&sigact.sa_mask);
    sigaction(SIGALRM, &sigact, NULL);

    // //結構成員it_value指定首次定時的時間,
    // // 結構成員it_interval指定下次定時的時間
    // // 定時器工做時,先將it_value的時間值減到0,
    // // 發送一個信號,再將it_value賦值爲it_interval的值,從新開始定時,如此反覆。
    struct itimerval itv;
    itv.it_interval.tv_sec = 2*timestep;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = 2*timestep;
    itv.it_value.tv_usec = 0;
    setitimer(ITIMER_REAL, &itv, NULL);
}
//設置uuid五秒切換插入順序的定時器
void set_timer2() 
{   
    struct sigaction sigact;
    sigact.sa_flags =  0;
    sigact.sa_handler = timeout_cb2; 
    sigemptyset(&sigact.sa_mask);
    sigaction(SIGALRM, &sigact, NULL);

    // //結構成員it_value指定首次定時的時間,
    // // 結構成員it_interval指定下次定時的時間
    // // 定時器工做時,先將it_value的時間值減到0,
    // // 發送一個信號,再將it_value賦值爲it_interval的值,從新開始定時,如此反覆。
    struct itimerval itv;
    itv.it_interval.tv_sec = timestep;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = timestep;
    itv.it_value.tv_usec = 0;
    setitimer(ITIMER_REAL, &itv, NULL);
}
int main(int argc, char const *argv[])
{
    set_timer();
    set_timer2();
    while(true)
    {
        // test_mutex.lock();
        // std::cout<<"               main lock"<<std::endl;
        // test_mutex.unlock();
    }

    return 0;
}

運行結果:code

  從運行結果中能夠看出,只有第二個定時器在運行,第一個被覆蓋了,這對於咱們想要同時設置兩種定時器的需求,貌似得不到解決(知識點的侷限,應該是咱們代碼有問題,還請大牛解答)。事件

    因此針對須要設置多個定時器,我最後推薦使用select,或者使用libevent庫中的定時器,具體參見例五,libevent的Timmer。get

舉例四:定時器回調函數只作標誌位置位操做,另開一個線程,根據標誌位作相應的操做。

#include <iostream>
#include <sys/time.h>
#include <signal.h>
#include <linux/types.h>
#include <sched.h>
#include <pthread.h> 

using namespace std;

//互斥鎖
class MutexLock
{
public:
    MutexLock(){ pthread_mutex_init(&m_stMutex, NULL); }
    ~MutexLock(){ pthread_mutex_destroy(&m_stMutex); }

    void lock(){  pthread_mutex_lock(&m_stMutex);}
    int unlock() {return pthread_mutex_unlock(&m_stMutex); }

    bool trylock(){ return pthread_mutex_trylock(&m_stMutex) == 0;}
    pthread_mutex_t* getMutexPtr(){ return &m_stMutex;}
private:
    pthread_mutex_t m_stMutex;
};


MutexLock test_mutex;
int timestep = 1;
bool time_is_now = false;


//五秒切換插入map順序
void timeout_cb(int sig)
{
   time_is_now = !time_is_now;
}


void set_timer() 
{   
    struct sigaction sigact;
    sigact.sa_flags =  0;
    sigact.sa_handler = timeout_cb; //定時器回調函數
    sigemptyset(&sigact.sa_mask);
    sigaction(SIGALRM, &sigact, NULL);

    // //結構成員it_value指定首次定時的時間,
    // // 結構成員it_interval指定下次定時的時間
    // // 定時器工做時,先將it_value的時間值減到0,
    // // 發送一個信號,再將it_value賦值爲it_interval的值,從新開始定時,如此反覆。
    struct itimerval itv;
    itv.it_interval.tv_sec = 2*timestep;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = 2*timestep;
    itv.it_value.tv_usec = 0;
    setitimer(ITIMER_REAL, &itv, NULL);
}


void* lokc_unlock(void * arg)
{
    while(true)
    {
        if(time_is_now)
        {
            test_mutex.lock();
            std::cout<<"  timer lock : "<<std::endl;
            test_mutex.unlock();
            time_is_now = false;// 置位
        }
    }
   
}


int main(int argc, char const *argv[])
{
    set_timer();

    pthread_t autorecv_child_thread;

    int res;
    if((res=pthread_create(&autorecv_child_thread,NULL,lokc_unlock, NULL))!=0)
    {
        std::cout<<"111111111111111"<<std::endl;
        return -1;
    }

    if( (res = pthread_detach(autorecv_child_thread) ) != 0 )
    {
        std::cout<<"2222222222222222"<<std::endl;
        return -1;
    }

    while(true)
    {
        usleep(100000);
        test_mutex.lock();
        std::cout<<"               main lock"<<std::endl;
        test_mutex.unlock();
    }

    return 0;
}

運行結果:

    從運行結果中能夠看出: 定時器回調函數只作簡單任務,複雜任務交由子線程處理,這樣不會出什麼問題。


舉例五:使用libevent的Timmer(即select模式使用定時任務)

    當須要多個定時任務同時定時操做的時候,由於本人暫時尚未找到setitimer的正確用法,所以不得不借用libevent的Timmer來實現,固然直接用select也能夠,能夠去搜索相應資料學習。

#include <iostream>
#include <sys/time.h>
#include <signal.h>
#include <linux/types.h>
#include <sched.h>
#include <pthread.h> 
#include <event2/event.h>
#include <event2/thread.h>
#include <event2/event-config.h>
using namespace std;

//互斥鎖
class MutexLock
{
public:
    MutexLock(){ pthread_mutex_init(&m_stMutex, NULL); }
    ~MutexLock(){ pthread_mutex_destroy(&m_stMutex); }

    void lock(){  pthread_mutex_lock(&m_stMutex);}
    int unlock() {return pthread_mutex_unlock(&m_stMutex); }

    bool trylock(){ return pthread_mutex_trylock(&m_stMutex) == 0;}
    pthread_mutex_t* getMutexPtr(){ return &m_stMutex;}
private:
    pthread_mutex_t m_stMutex;
};

MutexLock test_mutex;
int timestep = 1;

//五秒切換插入map順序
void timeout_cb(int fd, short event, void *params)
{
    test_mutex.lock();
    std::cout<<"   timmer1  lock"<<std::endl;
    test_mutex.unlock();
}

void timeout_cb2(int fd, short event, void *params)
{
    test_mutex.lock();
    std::cout<<"                  timmer2  lock"<<std::endl;
    test_mutex.unlock();
}


void* lokc_unlock(void * arg)
{
    event_base* base = event_base_new(); 

    struct event *timeout = NULL;
    struct timeval tv = {timestep, 0};
    timeout = event_new(base, -1, EV_PERSIST, timeout_cb, NULL);
    evtimer_add(timeout, &tv);

    struct event *timeout2 = NULL;
    struct timeval tv2 = {2*timestep, 0};
    timeout2 = event_new(base, -1, EV_PERSIST, timeout_cb2, NULL);
    evtimer_add(timeout2, &tv2);

    event_base_dispatch(base); 
    event_base_free(base);
}


int main(int argc, char const *argv[])
{
    pthread_t autorecv_child_thread;

    int res;
    if((res=pthread_create(&autorecv_child_thread,NULL,lokc_unlock, NULL))!=0)
    {
        std::cout<<"111111111111111"<<std::endl;
        return -1;
    }

    if( (res = pthread_detach(autorecv_child_thread) ) != 0 )
    {
        std::cout<<"2222222222222222"<<std::endl;
        return -1;
    }

    while(true)
    {
       
    }

    return 0;
}


運行結果:

從最後結果中看,本人仍是比較推薦使用libevent這種庫,畢竟使用簡單,功能強大。

ps:固然對於setitimer()來講,它支持延遲開啓定時器,即itimerval 的 it_intercal 和it_value的含義,libevent對於相應的支持就須要各位本身動腦筋想辦法了。(歡迎各位指教)

相關文章
相關標籤/搜索