Fork me on GitHub

Linux下定时器的使用

Linux下应用层定时器本来有好几种,大伙可以去搜索其他帖子博客,这里我主要描述我在使用setitimer时候遇到的问题,话不多说,直接上代码吧

例子一:只有定时器任务,为模拟复杂,我特意加个锁操作

// lock_timmer_test.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#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调度问题和定时事件太短的而造成不确定因素。  

所以在使用setitimer()的时候,我们尽量**不要让回调函数做过于复杂的操作,最好只是做某些标志位的置位操作就好。**

详情参见例四。

举例三:使用setitimer()定时器的时候,貌似不支持多个定时器的设置,详情看代码及运行结果,具体原因是什么,还请各位知道的指教指教。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#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;
}

运行结果:

从运行结果中可以看出,只有第二个定时器在运行,第一个被覆盖了,这对于我们想要同时设置两种定时器的需求,貌似得不到解决(知识点的局限,应该是我们代码有问题,还请大牛解答)。

所以针对需要设置多个定时器,我最后推荐使用select,或者使用libevent库中的定时器,具体参见例五,libevent的Timmer。

举例四:定时器回调函数只做标志位置位操作,另开一个线程,根据标志位做相应的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#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也可以,可以去搜索相应资料学习。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#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对于相应的支持就需要各位自己动脑筋想办法了。(欢迎各位指教)

感谢您的赞赏,赚点辛苦奶粉钱