使用epoll监测定时器是否到达指定时间,并执行回调函数

总览:Linux提供了定时器,暴露出来了文件描述符,所以我们使用epoll帮助我们监测,时间到达后,epoll_wait返回,于是我们根据fd,找到对应的回调函数,然后执行。从而达到定时执行函数的目的,后续我们打算将它添加到线程池中。本来我们的线程池如果遇到定时任务是睡眠n秒,然后执行,但是显而易见:这对于线程资源来说就是巨大的浪费 。这样问题就得到了改善。

架构:

代码:

mainTest.cpp

// C++ STL
#include <thread>
#include <iostream>
using namespace std;
#include "TimerManage.hpp"
void func(int x)
{
  static int num = 0;
  cout << "func " << x << " num: " << ++num << endl;
}
int main()
{
  myspace::Timer t1;
  myspace::TimerMap tmp;
  t1.init();
  t1.set_timer(std::bind(func, 1), 1250);
  tmp.init(-1);
  tmp.add_timer(t1);

  myspace::Timer t2;
  t2.init();
  t2.set_timer(std::bind(func, 100), 2250);
  tmp.add_timer(t2);
  
  while (1)
  {
    static int seconds = 0;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    cout << "___________________________main while: " << ++seconds << " s" << endl;
  }
  return 0;
}

Timer.hpp

//一个Timer对应一个任务,但是线程池中任务众多,所以我们需要对他做封装,搞出来一个Timer的管理者,所以就有了TimerManager,并且我们还要让epoll参与管理这么多fd(Timer对应一个fd)
#include <functional>
using namespace std;
#ifndef TIMER_HPP
#define TIMER_HPP
namespace myspace
{
  class Timer
  {
  public:
    using TimerCallback = std::function<void(void)>;

  private:
    int m_timerId;
    TimerCallback m_callback;
    int tag; // 1 out; 2 inter;
    bool settimer(size_t interval);

  public:
    Timer();
    ~Timer();
    Timer(const Timer &) = delete;
    Timer &operator=(const Timer &) = delete;
    Timer(Timer &&);
    Timer &operator=(Timer &&);
    bool init();
    bool set_timer(const TimerCallback &cb, size_t interval);
    bool reset_timer(size_t interval);
    void handle_event();
    int get_TimerId() const;
    int close_timer();
  };
}

#endif

Timer.cpp



// Liunx API
#include <sys/timerfd.h>
// C API
#include <unistd.h>
#include <stdio.h>
#include <string.h>
// C++ STL;
#include <iostream>
using namespace std;
#include "Timer.hpp"

namespace myspace
{
  // class Timer
  // using TimerCallback = std::function<void(void)>;
  // int m_timerId;
  // TimerCallback m_callback;
  // int tag; // 1 out; 2 inter;
  bool Timer::settimer(size_t interval) // 1250
  {
    bool ret = true;
    struct itimerspec new_value = {0};
    new_value.it_interval.tv_sec = (interval / 1000); // secodes;
    new_value.it_interval.tv_nsec = (interval % 1000) * 1000 * 1000;
    new_value.it_value = new_value.it_interval;
    if (timerfd_settime(m_timerId, 0, &new_value, nullptr) < 0)
    {
      fprintf(stderr, "failer error : %s \n", strerror(errno));
      ret = false;
    }
    return ret;
  }

  Timer::Timer() : m_timerId(-1), m_callback(nullptr) {}
  Timer::~Timer() { close_timer(); }
  Timer::Timer(Timer &&other)
      : m_timerId(other.m_timerId),
        m_callback(other.m_callback)
  {
    other.m_timerId = -1;
    other.m_callback = nullptr;
  }
  Timer &Timer::operator=(Timer &&other)
  {
    if (this == &other)
      return *this;
    //??
    m_timerId = other.m_timerId;
    m_callback = other.m_callback;
    other.m_timerId = -1;
    other.m_callback = nullptr;
    return *this;
  } //
  bool Timer::init()
  {
    bool ret = true;
    if (m_timerId > 0)
    {
      return ret;
    }
    m_timerId = timerfd_create(CLOCK_MONOTONIC, 0);
    if (m_timerId < 0)
    {
      ret = false;
    }
    return ret;
  }
  bool Timer::set_timer(const TimerCallback &cb, size_t interval)
  {
    bool ret = false;
    if (m_timerId > 0 && settimer(interval))
    {
      m_callback = cb;
      ret = true;
    }
    return ret;
  }
  bool Timer::reset_timer(size_t interval)
  {
    bool ret = false;
    if (m_timerId > 0 && m_callback != nullptr && settimer(interval))
    {
      ret = true;
    }
    return ret;
  }
  void Timer::handle_event()
  {
    uint64_t expire_cnt = 0;
    if (read(m_timerId, &expire_cnt, sizeof(expire_cnt)) != sizeof(expire_cnt)) // 定时器到期时,会在这个文件描述符上产生可读事件。read 用来消耗这个事件,epoll就不管了。这和网络中的epoll一样,你要read或者recv一下。而read的这个值就是到期的次数,:定时器到期一次,返回1,然后你去缓冲区中去消耗这个1

    {
      return;
    }
    cout << "expire_cnt : " << expire_cnt << endl;
    if (m_callback != nullptr)
    {
      m_callback();
    }
  }
  int Timer::get_TimerId() const
  {
    return m_timerId;
  }
  int Timer::close_timer()
  {
    bool ret = false;
    if (m_timerId > 0)
    {
      close(m_timerId);
      m_timerId = -1;
      m_callback = nullptr;
      ret = true;
    }
    return ret;
  }
}

TimerManage.hpp

// ONWER

#include "Timer.hpp"
// LIUNX API
#include <sys/epoll.h>
// C++ STL
#include <thread>
#include <map>
#include <unordered_map>
#include <vector>

#ifndef TIMER_MANAGE_HPP
#define TIMER_MANAGE_HPP

namespace myspace
{
  class TimerMap
  {
  private:
    int m_epollfd;
    std::vector<epoll_event> m_events;
    std::unordered_map<int, myspace::Timer> m_timers;
    bool m_stop; // true stop;
    static const int eventsize = 16;
    std::thread m_workerThread;

  public:
    TimerMap();
    ~TimerMap();
    TimerMap(const TimerMap &) = delete;
    TimerMap &operator=(const TimerMap &) = delete;
    bool init(int timeout);
    bool add_timer(Timer &tv);
    void remove_timer(int fd);
    void loop(int timeout);
    void set_stop();
  };
}

#endif

TimerManage.cpp


#include "TimerManage.hpp"
// C API
#include <string.h>
#include <unistd.h>
//C++ API
#include<iostream>
using namespace std;
namespace myspace
{
  // TimerMap
  // int m_epollfd;
  // std::vector<epoll_event> m_events;
  // std::unordered_map<int, myspace::Timer> m_timers;
  // bool m_stop; // true stop;
  // static const int eventsize = 16;
  // std::thread m_workerThread;
  TimerMap::TimerMap()
      : m_epollfd(-1), m_stop(false)
  {
    m_events.resize(eventsize);
  }
  TimerMap::~TimerMap()
  {
    set_stop();
  }

  bool TimerMap::init(int timeout)
  {
    bool ret = false;
    m_epollfd = epoll_create1(EPOLL_CLOEXEC);
    if (m_epollfd > 0)
    {
      try
      {
        m_workerThread = std::thread(&TimerMap::loop, this, timeout);
        ret = true;
      }
      catch (const std::exception &e)
      { 
        cout << e.what() << '\n';
        close(m_epollfd);
        m_epollfd = -1;
      }
    }
    return ret;
  }

  bool TimerMap::add_timer(Timer &tv)
  {
    struct epoll_event evt;
    evt.data.fd = tv.get_TimerId();
    evt.events = EPOLLIN | EPOLLET;
    if (epoll_ctl(m_epollfd, EPOLL_CTL_ADD, tv.get_TimerId(), &evt) < 0)
    {
      fprintf(stderr, "timer_manager: epoll_ctl_add failed, errno = %s\n",
              strerror(errno));
      return false;
    }
    m_timers[tv.get_TimerId()] = std::move(tv);
    return true;
  }
  void TimerMap::remove_timer(int fd)
  {
    if(m_timers.find(fd)==m_timers.end())
      return;
    epoll_ctl(fd, EPOLL_CTL_DEL, fd, nullptr);
    m_timers.erase(fd);    
  }
  void TimerMap::loop(int timeout)
  {
    while (!m_stop)
    {
      int n = epoll_wait(m_epollfd, m_events.data(), m_events.size(), timeout);//timeout==-1表示阻塞等待
      for (int i = 0; i < n; ++i)
      {
        int fd = m_events[i].data.fd;
        auto it = m_timers.find(fd);
        if (it != m_timers.end())
        {
          Timer &tv = it->second;
          tv.handle_event();
        }
      }
      if (n >= m_events.size())
      {
        m_events.resize(m_events.size() * 2);
      }
    }
  }
  void TimerMap::set_stop()
  {
    m_stop = true;
    if (m_workerThread.joinable())
    {
      m_workerThread.join();
    }
    close(m_epollfd);
    m_epollfd = -1;
  }
}

编译命令:

g++ -o output mainTest.cpp Timer.cpp TimerManage.cpp -std=c++11 -lpthread

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/927394.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙征文|鸿蒙技术分享:使用到的开发框架和技术概览

目录 每日一句正能量前言正文1. 开发环境搭建关键技术&#xff1a;2. 用户界面开发关键技术&#xff1a;3. 应用逻辑开发关键技术&#xff1a;4. 应用测试关键技术&#xff1a;5. 应用签名和打包关键技术&#xff1a;6. 上架流程关键技术&#xff1a;7. 后续维护和更新关键技术…

【MIT-OS6.S081笔记0.5】xv6 gdb调试环境搭建

补充一下xv6 gdb调试环境的搭建&#xff0c;我这里装的是最新的15.2的gdb的版本。我下载的是下面的第二个xz后缀的文件&#xff1a; 配置最详细的步骤可以参考下面的文章&#xff1a; [MIT 6.S081] Lab 0: 实验配置, 调试及测试 这里记录一下踩过的一些报错&#xff1a; 文…

Python和Java后端开发技术对比

在当今互联网技术飞速发展的时代&#xff0c;后端开发扮演着至关重要的角色。Python和Java作为两大主流的后端开发语言&#xff0c;各自具备独特的优势和应用场景。让我们深入了解这两种技术的特点和选择建议。 Java后端开发一直是企业级应用的首选方案。它以强大的类型系统、…

1.2.3 逻辑代数与运算

逻辑代数与运算 基本的逻辑运算常用逻辑公式 基本的逻辑运算 基本逻辑运算非常简单&#xff0c;只包含与、或、非、异或这4种。 这里主要留意对基本逻辑运算的不同叫法&#xff0c;符号表示。逻辑表达式、真值表概念。 与&#xff1a;A和B都为真时&#xff0c;结果才为真或…

解析生成对抗网络(GAN):原理与应用

目录 一、引言 二、生成对抗网络原理 &#xff08;一&#xff09;基本架构 &#xff08;二&#xff09;训练过程 三、生成对抗网络的应用 &#xff08;一&#xff09;图像生成 无条件图像生成&#xff1a; &#xff08;二&#xff09;数据增强 &#xff08;三&#xff…

零售餐饮收银台源码

收银系统早已成为门店经营的必备软件工具&#xff0c;因为各个连锁品牌有自己的经营模式&#xff0c;自然对收银系统需求各有不同&#xff0c;需要有相应的功能模块来实现其商业模式。 1. 适用行业 收银系统源码适用于零售、餐饮等行业门店&#xff0c;如商超便利店、水果生鲜…

我的第一个创作纪念日 —— 梦开始的地方

前言 时光荏苒&#xff0c;转眼间&#xff0c;我已经在CSDN这片技术沃土上耕耘了365天 今天&#xff0c;我迎来了自己在CSDN的第1个创作纪念日&#xff0c;这个特殊的日子不仅是对我过去努力的肯定&#xff0c;更是对未来持续创作的激励 机缘 回想起初次接触CSDN&#xff0c;那…

mac终端自定义命令打开vscode

1.打开终端配置文件 open -e ~/.bash_profile终端安装了zsh&#xff0c;那么配置文件是.zshrc&#xff08;打开zsh配置&#xff0c;这里举&#x1f330;使用zsh&#xff09; sudo open -e ~/.zshrc 2.在zshrc配置文件中添加新的脚本&#xff08;这里的code就是快捷命令可以进…

计算帧率、每秒过多少次

1、c #include <iostream> #include <opencv2/opencv.hpp> #include <string> #include <thread> #include <atomic>using namespace std;const int NUM_THREADS 1; // 线程数量std::atomic<int> frameCounts[NUM_THREADS]; // 每个线程…

【在Linux世界中追寻伟大的One Piece】读者写者问题与读写锁

目录 1 -> 读者写者问题 1.1 -> 什么是读者写者问题 1.2 -> 读者写者与生产消费者的区别 1.3 -> 如何理解读者写者问题 2 -> 读写锁 2.1 -> 读写锁接口 3 -> 读者优先(Reader-Preference) 4 -> 写者优先(Writer-Preference) 1 -> 读者写者…

PS的功能学习(修复、画笔)

混合器画笔工具 就像&#xff0c;电子毛笔 关键功能有两个&#xff0c;自带一个混合器色板 清理画笔是全清&#xff0c;换一支新的毛笔&#xff0c;执行完之后在判断是否载入画笔 载入画笔就是把前景色上的颜色进行叠加处理&#xff0c;重新混入当前的混合色 &#xff08;…

centos 7 离线安装postgis插件

前一段时间记录了下如何在centos7中离线安装postgresql&#xff0c;因为工作需要&#xff0c;我不仅要安装postgresql&#xff0c;还需要安装postgis插件&#xff0c;这篇文章记录下postgis插件的安装过程。 1. 安装前的参考 如下的链接都是官网上的链接&#xff0c;对你安装p…

Vue 90 ,Element 13 ,Vue + Element UI 中 el-switch 使用小细节解析,避免入坑(获取后端的数据类型自动转变)

目录 前言 在开发过程中&#xff0c;我们经常遇到一些看似简单的问题&#xff0c;但有时正是这些细节问题让我们头疼不已。今天&#xff0c;我就来和大家分享一个我在开发过程中遇到的 el-switch 使用的小坑&#xff0c;希望大家在使用时能够避免。 一. 问题背景 二. 问题分…

同时使用Tmini和GS2两个雷达

24.12.02 要求&#xff1a;同时使用两个雷达。 问题在于:两个雷达都是ydlidar&#xff0c;使用同一个包。 因此同时启动GS2.launch和Tmini.launch会调用同一个功能节点&#xff0c;使用同一个cpp文件。 方法&#xff1a;新建一个cpp节点。 但同时保持在同一个坐标系&#xff0…

高等数学函数的性质

牛顿二项公式 ( x y ) n ∑ k 0 n C n k ⋅ x n − k y k (xy)^n\stackrel{n}{\sum\limits_{k0}}C^k_n\sdot x^{n-k}y^k (xy)nk0∑​n​Cnk​⋅xn−kyk. 映射 f : X → Y f:X\rightarrow Y f:X→Y&#xff0c; f f f 为 X X X 到 Y Y Y 的映射。 f f f 是一个对应关系&am…

【MySQL】深度学习数据库开发技术:mysql事务穿透式解析

前言&#xff1a;本节内容开始讲解事务。 博主计划用三节来讲解事务。 本篇为第一节&#xff0c; 主要解释什么是事务&#xff0c; 事务有什么用。 以及事物的基本操作和异常退出回滚情况。 下面不多说&#xff0c;友友们&#xff0c; 开始学习吧&#xff01; ps&#xff1a;本…

Swift解题 | 求平面上同一条直线的最多点数

文章目录 前言摘要问题描述解题思路Swift 实现代码代码分析示例测试与结果时间复杂度空间复杂度总结关于我们 前言 本题由于没有合适答案为以往遗留问题&#xff0c;最近有时间将以往遗留问题一一完善。 149. 直线上最多的点数 不积跬步&#xff0c;无以至千里&#xff1b;不积…

使用Ansible自动化部署Zabbix6监控

1、获取Ansible离线部署包 链接&#xff1a;https://pan.baidu.com/s/1EjI02Ni8m9J4eJeBcJ-ZUQ?pwdzabx 提取码&#xff1a;zabx 2、安装Ansible wget -O /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo yum -y install ansible3、修改hosts文件…

lua闭包Upvalue

闭包 lua任何函数都是闭包&#xff0c;闭包至少带1个upValue&#xff1b; CClosure是使用Lua提供的lua_pushcclosure这个C-Api加入到虚拟栈中的C函数&#xff0c;它是对LClosure的一种C模拟 如string.gmatch就是cclosure 定义&#xff1a; #define ClosureHeader \CommonH…

二叉搜索树之遍历

二叉搜索树是一种重要的数据结构&#xff0c;它的每个节点最多有两个子节点&#xff0c;称为左子节点和右子节点。 二叉搜索树的特性是&#xff1a;对于树中的每个节点&#xff0c;其左子树中的所有节点的值都小于该节点的值&#xff0c;而右子树中的所有节点的值都大于该节点…