Linux下应用层定时器本来有好几种,大伙可以去搜索其他帖子博客,这里我主要描述我在使用setitimer时候遇到的问题,话不多说,直接上代码吧
例子一:只有定时器任务,为模拟复杂,我特意加个锁操作
// lock_timmer_test.cpp
#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()定时器的时候,貌似不支持多个定时器的设置,详情看代码及运行结果,具体原因是什么,还请各位知道的指教指教。
#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。
举例四:定时器回调函数只做标志位置位操作,另开一个线程,根据标志位做相应的操作。
#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对于相应的支持就需要各位自己动脑筋想办法了。(欢迎各位指教)