文档章节

Linux下定时器的使用

sysu_huyh5
 sysu_huyh5
发布于 2016/03/31 14:30
字数 1863
阅读 118
收藏 3

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对于相应的支持就需要各位自己动脑筋想办法了。(欢迎各位指教)



© 著作权归作者所有

sysu_huyh5
粉丝 6
博文 13
码字总数 13030
作品 0
深圳
程序员
私信 提问
Netty的Timer管理–开源的魅力

这里的Timer,是指定时器,现代操作系统,定时器无处不在,以至于有些将linux kernel的书,都需要单独列出一章,来将linux是如何管理这些定时器的。管理定时器其实主要的步骤有以下3步: 1....

gongweixin
2013/08/26
0
0
linux设备驱动时间、延迟及延缓操作

hz:上述间隔由hz的值设定,hz是一个与体系结构相关的常数 计数器:发生中断一次,计数器加一,这个计数器的值(只有)在系统引导时被初始化为0 jiffies变量:unsigned long 型变量,要么与jif...

慎思
2012/08/10
0
0
高精度计时器,windows,linux,voworks

本文主要介绍Linux下高精度时间函数,及相关的具有超时机制的函数,对定时器也进行简单的介绍。 在linux下通常可用的精度最高的时间接口是gettimeofday,它返回一个timeval结构,其精度为us,...

brucema
2012/01/05
0
0
Linux设备驱动——内核定时器

内核定时器使用 内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,其实现位于 和 kernel/timer.c 文件中。 被调度的函数肯定是异步执行的,它类似于一种...

wb3553
2018/05/29
0
0
在 Linux 上使用 systemd 设置定时器

学习使用 systemd 创建启动你的游戏服务器的定时器。 之前,我们看到了如何手动的、在开机与关机时、在启用某个设备时、在文件系统发生改变时 启用与禁用 systemd 服务。 定时器增加了另一种...

作者: Paul Brown
2018/11/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Linux输入法fcitx的安装问题

Fcitx 总共要安装的包如下 fcitxfcitx-binfcitx-config-commonfcitx-config-gtk | fcitx-config-gtk2fcitx-datafcitx-frontend-allfcitx-frontend-gtk2fcitx-frontend-gtk3......

CHONGCHEN
40分钟前
3
0
网络基础

前言: 最近整理一些以前的学习笔记(有部分缺失,会有些乱,日后再补)。 过去都是存储在本地,此次传到网络留待备用。 计算机网络的功能: 1.数据通信; 2.资源共享; 3.增加数据可靠性; 4....

迷失De挣扎
40分钟前
3
0
spring boot升级到spring cloud

1、先升级spring boot 版本到2.1.3 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.1.3.RELEAS......

moon888
44分钟前
10
0
从蓝鲸视角谈DevOps

DevOps源于Development和Operations的组合 常见的定义 DevOps是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。透过自动化“软件交付”和“架构变...

嘉为科技
46分钟前
1
0
微服务设计 笔记

微服务设计 一、微服务架构理论 1.六边形架构 1)六边形架构(Hexagonal Architecture),又称为端口和适配器架构风格;使用适配器与外界进行交互,外界通过应用层API与内部进行交互。 2)经典...

啃不动地大坚果
55分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部