zoukankan      html  css  js  c++  java
  • 0804-----Linux基础----------定时器之 timerfd

    1.timerfd 的基本用法

      1.1 timerfd 系列的定时器采用的不是信号,而是 fd 可读,常用的函数有 timerfd_create 、timerfd_settime 和 timerfd_gettime, 这些函数的功能和用法也比较浅显,这里用一个简单的例子(1.2)来说明其用法,create 参数中 CLOCK_REALTIME 是一个相对时间,当系统时间被更改时会进行调整,还有一个参数CLOCK_MONOTONIC,它是绝对时间,更改系统时间对它没有影响,这里flags一般设为0,前面说到,定时器到期表现为 timerfd 可读,因此就可以把该fd加入到IO复用模型当中去(1.3)。

      1.2 创建一个定时器,设置好时间间隔结构体的参数,开启定时器,这样一个定时器就可以工作了,但是这里要注意,timerfd 定时器到期时表现为该 fd 有数据可读,因此一旦到时,一定要把数据 read 出去,不然定时器就无法正常工作。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/timerfd.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    /*
     * 定时器timerfd的基础用法
     */
    
    int main(int argc, const char *argv[])
    {
        //创建定时器的fd
        int timerfd = timerfd_create(CLOCK_REALTIME, 0);
        if(timerfd == -1)
            ERR_EXIT("timerfd_create");
        //开启定时器,并设置定时器的时间
        struct itimerspec new_value; // const
        memset(&new_value, 0, sizeof new_value);
        new_value.it_value.tv_sec = 5; //初始到期时间
        new_value.it_interval.tv_sec = 1; //之后的间隔时间
        if(timerfd_settime(timerfd, 0, &new_value, NULL) == -1)
            ERR_EXIT("timerfd_settime");
    
        char buf[1024] = {0};
        int ret;
        while((ret = read(timerfd, buf, sizeof buf)) > 0){
            printf("ret = %d, read data:%s
    ", ret, buf); //
        }
    
        close(timerfd);
        return 0;
    }

      1.3 将timer和poll一起使用,timerfd 加入到 poll 的监听数组中,当 timer 到期,poll 就会监听到该 timerfd。这里 poll 采用的是水平触发模式,也就是说对于某 fd 可读,如果不做 read 处理,那么下次会再次返回该 fd,即该 fd一直可读,等待着被读出去,如果不读出去,就一直被触发,那么定时器就没用了。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <poll.h>
    #include <sys/timerfd.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    /*
     * 定时器timerfd 和 epoll 一起使用
     */
    
    int main(int argc, const char *argv[])
    {
        //创建定时器的fd
        int timerfd = timerfd_create(CLOCK_REALTIME, 0);
        if(timerfd == -1)
            ERR_EXIT("timerfd_create");
        //开启定时器,并设置定时器的时间
        struct itimerspec new_value; // const
        memset(&new_value, 0, sizeof new_value);
        new_value.it_value.tv_sec = 5; //初始到期时间
        new_value.it_interval.tv_sec = 1; //之后的间隔时间
        if(timerfd_settime(timerfd, 0, &new_value, NULL) == -1)
            ERR_EXIT("timerfd_settime");
    
        char buf[1024] = {0};
        struct pollfd event[1];
        event[0].fd = timerfd;
        event[0].events = POLLIN;
    
        while(1){
            int ret =  poll(event, 1, 10000);//等待的最长时间是10s
            if(ret == -1)
                ERR_EXIT("poll");
            else if(ret == 0)
                printf("timeout
    ");
            else{
                if(read(timerfd, buf, sizeof buf) == -1) //这里如果不read 会一直被触发
                    ERR_EXIT("read");
                printf("foobar....
    ");
            }
        }
    }

      1.4 一个Timer类,这里把 timerfd 类的函数封装成类,由用户提供定时器到期时要执行的操作(即使用函数回调),这样做的好处,也就是面向对象的好处,对外界提供接口函数,需要时直接用对象调用,更加安全。

    #ifndef __TIMER_H__
    #define __TIMER_H__
    
    #include "NonCopyable.h"
    #include <functional>
    #include <sys/timerfd.h>
    
    class Timer : NonCopyable{
        public:
            typedef std::function<void ()> TimerCallback; //定时器回调函数类型
            Timer();
            ~Timer();
    
            void setTimer(int  init_val, int inter_val); //参数为初始到期时间 和 以后每次的间隔时间
            void setCallback(const TimerCallback &callback);
            void runTimer();
    
        private:
            int timerfd_;
            struct itimerspec howlong_;
            TimerCallback  callback_; //用户自定义的逻辑
    };
    
    
    
    #endif  /*__TIMER_H__*/
    #include "Timer.h"
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <poll.h>
    #include <iostream>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    
    
    Timer::Timer(){
        timerfd_ = timerfd_create(CLOCK_REALTIME, 0);
        if(timerfd_ == -1)
            ERR_EXIT("timer_create");
        memset(&howlong_, 0, sizeof howlong_);
    }
    
    Timer::~Timer(){
        close(timerfd_);
    }
    
    void Timer:: setTimer(int init_val, int inter_val){ //参数
        howlong_.it_value.tv_sec = init_val;
        howlong_.it_interval.tv_sec = inter_val;
    }
    
    void Timer::setCallback(const TimerCallback &callback){
        callback_ = callback;
    }
    
    void Timer::runTimer(){
        //开启定时器
        if(timerfd_settime(timerfd_, 0, &howlong_, NULL) == -1)
            ERR_EXIT("timerfd_settime");
    
        struct pollfd event[1];
        event[0].fd = timerfd_;
        event[0].events = POLLIN;
        char buf[1024];
        int ret;
    
        while(1){
           ret = poll(event, 1, 1000); //每隔1s 轮询一次
           if(ret == -1)
               ERR_EXIT("poll");
           else if(ret == 0)
               std::cout << "timerout" << std::endl;
           else{
               if(read(timerfd_, buf, sizeof buf)== -1)
                   ERR_EXIT("read");
                callback_(); //调用用户逻辑
           }
        }
    }
    
    
    
    #include "Timer.h"
    #include <iostream>
    using namespace std;
    
    void func(){
        cout << "hello world" << endl;
    }
    
    int main(int argc, const char *argv[])
    {
        Timer tm;
        tm.setTimer(3, 1);
        tm.setCallback(func);
        tm.runTimer();
        return 0;
    }

    2.Timer 和 Thread 类的组合

      2.1 程序编写思路:

        a)把用户逻辑 bind 到 Timer 里面;

        b)把Timer 的 runTimer 方法bind 到 Thread 里面;

        c)也就是说,把 Timer 的操作封装到线程里,执行线程也就是开启定时器。 

      2.2 源程序清单

        a)Thread.h Thread.cpp

    #ifndef __THREAD_H__
    #define __THREAD_H__
    #include "NonCopyable.h"
    #include <pthread.h>
    #include <functional>
    
    class Thread : NonCopyable{
        public:
            typedef std::function<void ()> ThreadCallback;
    
            Thread();
            explicit Thread(const ThreadCallback &callback);
            ~Thread();
    
            void setCallback(const ThreadCallback &callback);
            void start();
            void join();
    
        private:
            static void *thread_func(void *);
    
            pthread_t tid_;
            ThreadCallback callback_;
            bool isStarted_;
    
    };
    
    #endif  /*__THREAD_H__*/
    #include "Thread.h"
    Thread::Thread()
        :tid_(-1),
         isStarted_(false)
    {
    }
    
    Thread::Thread(const ThreadCallback &callback)
        :tid_(-1),
         isStarted_(false),
         callback_(callback)
    {
    }
    
    Thread::~Thread(){
        if(isStarted_)
            pthread_detach(tid_);
    }
    
    void Thread::setCallback(const ThreadCallback &callback){
        callback_ = callback;
    }
    
    void Thread::start(){
        isStarted_ = true;
        pthread_create(&tid_, NULL, thread_func, this);
    }
    
    void Thread::join(){
        isStarted_ = false;
        pthread_join(tid_, NULL);
    }
    
    void *Thread::thread_func(void *arg){
        Thread *pt = static_cast<Thread *>(arg);
        pt->callback_();
        return NULL;
    }

        b) test_timer.cpp

    #include "Timer.h"
    #include "Thread.h"
    #include <iostream>
    using namespace std;
    /*
     * 使用类的组合 Thread 和 Timer 类
     */
    class TimerThread{
        public:
            TimerThread();
            void print(); //定时器到期时 执行的函数
            void startTimerThread();
        private:
            Thread thread_;
            Timer timer_;
    };
    
    TimerThread::TimerThread(){}
    
    void TimerThread::print(){
        cout << "hello world" << endl;
    }
    
    void TimerThread::startTimerThread(){
        timer_.setTimer(3, 1);
        timer_.setCallback(bind(&TimerThread::print, this));
    
        // 线程类用来封装Timer的操作
        thread_.setCallback(bind(&Timer::runTimer, &timer_));
        thread_.start();
        thread_.join();
    }
    
    int main(int argc, const char *argv[])
    {
        TimerThread tt;
        tt.startTimerThread();
        return 0;
    }
    

      

  • 相关阅读:
    缺陷与出路——一个游戏开发者的反思(转自《大众软件》)
    Arcengine 根据坐标串生成几何图形
    C#Arcengine通过坐标点生成面(环形)
    解析XML文件
    arcgis下载地址
    C#读写txt文件的两种方法介绍
    可伸缩性最佳实践:来自eBay的经验[精华强贴, 转之]
    请问怎样才能监视数据库表的变化?[转]
    VS2010中,在新建项目的时候,删除默认新建路径或曾经使用过的路径
    VS2010注册表垃圾清理
  • 原文地址:https://www.cnblogs.com/monicalee/p/3891323.html
Copyright © 2011-2022 走看看