timerfd是Linux为用户程序提供的一个定时器接口,这个接口基于文件描述符。

clock_gettime函数可以获取系统时钟,精确到纳秒。需要在编译时指定库:-lrt。可以获取两种类型时间:

CLOCK_REALTIME:相对时间,从1970.1.1到目前的时间。更改系统时间会更改获取的值。也就是,它以系统时间为坐标。
CLOCK_MONOTONIC:与CLOCK_REALTIME相反,它是以绝对时间为准,获取的时间为系统重启到现在的时间,更改系统时间对齐没有影响。

timerfd接口:

int timerfd_create(int clockid, int flags);

timerfd_create生成一个定时器对象,返回与之关联的文件描述符

参数clockid可以是CLOCK_MONOTONIC或者CLOCK_REALTIME。

参数flags可以是0或者O_CLOEXEC/O_NONBLOCK。

函数返回值是一个文件句柄fd。

int timerfd_settime(int ufd, int flags, const struct itimerspec * utmr, struct itimerspec * otmr);

timerfd_settime函数用于设置新的超时时间,并开始计时

参数ufd是timerfd_create返回的文件句柄。

参数flags为TFD_TIMER_ABSTIME代表设置的是绝对时间;为0代表相对时间。

参数utmr为超时时间

参数otmr为定时器这次设置之前的超时时间。

函数返回0代表设置成功。

int timerfd_gettime(int ufd, struct itimerspec * otmr);

timerfd_gettime函数用于获得定时器距离下次超时还剩下的时间。如果调用时定时器已经到期,并且该定时器处于循环模式(设置超时时间时struct itimerspec::it_interval不为0),那么调用此函数之后定时器重新开始计时。

read

当timerfd为阻塞方式时,read函数将被阻塞,直到定时器超时。

函数返回值大于0,代表定时器超时;否则,代表没有超时

数据结构:

struct timespec
{
time_t tv_sec; /* Seconds */
long tv_nsec; /* Nanoseconds */
}; struct itimerspec
{
struct timespec it_interval; /* Interval for periodic timer */
struct timespec it_value; /* Initial expiration */
};

it_value是首次超时时间,需要填写从clock_gettime获取的时间,并加上要超时的时间。 it_interval是后续周期性超时时间,即如果是周期性定时器,第一个超时时间是 it_value,后面所有周期的超时时间为it_interval

it_interval不为0则表示是周期性定时器,it_value和it_interval都为0表示停止定时器。
#include <sys/timerfd.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h> /* Definition of uint64_t */ #define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while () void printTime()
{
struct timeval tv;
gettimeofday(&tv, NULL);
printf("printTime: current time:%ld.%ld ", tv.tv_sec, tv.tv_usec);
} int main(int argc, char *argv[])
{
struct timespec now;
if (clock_gettime(CLOCK_REALTIME, &now) == -)
handle_error("clock_gettime");
//设置超时时间
struct itimerspec new_value;
new_value.it_value.tv_sec = now.tv_sec + atoi(argv[]);
new_value.it_value.tv_nsec = now.tv_nsec;
new_value.it_interval.tv_sec = atoi(argv[]);
new_value.it_interval.tv_nsec = ; int fd = timerfd_create(CLOCK_REALTIME, );
if (fd == -)
handle_error("timerfd_create"); if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -)
handle_error("timerfd_settime"); printTime();
printf("timer started\n"); for (uint64_t tot_exp = ; tot_exp < atoi(argv[]);)
{
uint64_t exp;
//阻塞等待定时器到期。返回值是未处理的到期次数。比如定时间隔为2秒,但过了10秒才去读取,则读取的值是5
ssize_t s = read(fd, &exp, sizeof(uint64_t));
if (s != sizeof(uint64_t))
handle_error("read"); tot_exp += exp;
printTime();
printf("read: %llu; total=%llu\n",exp, tot_exp);
} exit(EXIT_SUCCESS);
}

编译运行:编译时要加rt库(g++ -lrt timerfd.cc -o timerfd)

[root@localhost appTest]# ./timerfd 5 2 10

http://blog.csdn.net/gdutliuyun827/article/details/8470777

http://blog.csdn.net/chgaowei/article/details/21295811

 

 7.8  定时器
在一般的服务端程序设计中,与时间有关的常见任务有:
1.获取当前时间,计算时间间隔===>使用gettimeofday来获取当前时间,时间精度是1微秒
2.定时操作===>使用timerfd来处理定时任务,把时间变成一个文件描述符,可以通过epoll进行管理,用统一的方式来处理IO事件和超时事件
通过最小堆来管理未到期的timers,所有操作的时间复杂度为O(logN) 
  • Timer类:它用来表示一个定时器,封装了定时器的超时时间(首次超时时间),间隔时间(后续周期性超时时间),回调函数等。
  • TimerQueue类:它表示一个定时器集合,包含当前所有定时器的集合,堆顶的超时时间最小。现在linux 内核中的定时器也可以使用文件描述符来表示了,但是对于堆中所有的定时器不能每个都开一个fd,这显然会造成fd的不够用,因此muduo的实现办法是用定时器堆顶的定时器来初始化timer_fd(只有一个timer_fd),当epoll监听到该timerfd对应的channel出现可读事件时,表示定时器到期,此时的处理办法就更巧妙了,因为可能存在超时时间和处理时间的误差,因此在处理这个定时器到期事件的同时,会查看当前定时器堆是否还有到期的定时器,对它们一并进行处理。
Timer类:
class Timer : boost::noncopyable
{
public: private:
const TimerCallback callback_;//定时器回调函数
Timestamp expiration_;//超时时间
const double interval_;//时间间隔
const bool repeat_;//周期个数
};

弄清楚最小堆

 
 
https://www.zhihu.com/question/32251997
http://coderfirefly.com/2015/09/17/150917_muduo_6/
https://www.ibm.com/developerworks/cn/linux/l-cn-timers/

服务器端采用非阻塞IO+IO多路复用,程序中有多个event loop(事件循环),每个线程至多有一个event loop,其中event loop用作IO多路复用,配合非阻塞IO和定时器使用.线程队列中的线程不用处理IO只需要执行计算任务。
muduo库里面的线程池是固定线程池,即创建的线程池里面的线程个数是一定的,不是动态的。线程池里面包含线程队列还有任务队列,外部程序将任务存放到线程池的任务队列中,线程池中的线程队列执行任务,是一种生产者和消费者模型(condition_variable、unique_lock)。
线程队列:boost::ptr_vector<muduo::Thread> threads_
任务队列:std::deque<Task> queue_
class ThreadPool : boost::noncopyable
{
public:
typedef boost::function<void ()> Task; explicit ThreadPool(const string& name = string());
~ThreadPool();
//启动线程池
void start(int numThreads);
//关闭线程池
void stop();
//运行任务,往线程池当中的任务队列添加任务
void run(const Task& f); private:
//线程池当中的线程要执行的函数
void runInThread();
//获取任务
Task take(); MutexLock mutex_;//和条件变量配合使用的互斥锁
Condition cond_;//条件变量用来唤醒线程池中的线程队列来执行任务
string name_;//线程池名称
boost::ptr_vector<muduo::Thread> threads_;//存放线程指针
std::deque<Task> queue_;//任务队列
bool running_;//线程池是否处于运行的状态
};

线程池实现文件ThreadPool.cc

//启动固定的线程池
void ThreadPool::start(int numThreads)
{
assert(threads_.empty());//断言当前线程池为空
running_ = true;//置线程池处于运行的状态
threads_.reserve(numThreads);//预留这么多个空间
for (int i = ; i < numThreads; ++i)
{//for循环创建线程
char id[];
//线程号
snprintf(id, sizeof id, "%d", i);
//创建线程并存放线程指针,绑定的函数为runInThread
threads_.push_back(new muduo::Thread(boost::bind(&ThreadPool::runInThread, this), name_+id));
threads_[i].start();//启动线程,即runInThread函数执行
}
}
//关闭线程池
void ThreadPool::stop()
{
{
MutexLockGuard lock(mutex_);
running_ = false;//running置为false
cond_.notifyAll();//通知所有线程
}
//等待线程退出
for_each(threads_.begin(),threads_.end(),boost::bind(&muduo::Thread::join, _1));
}
//添加任务
void ThreadPool::run(const Task& task)
{//将任务添加到线程池当中的任务队列
if (threads_.empty())//如果线程池当中的线程是空的
{
task();//直接执行任务===>我的理解:event loop所在线程或线程池中的线程都有可能往任务队列中添加任务
}
else//否则添加
{
MutexLockGuard lock(mutex_);
queue_.push_back(task);
cond_.notify();//通知队列当中有任务了
}
}
//获取任务函数
ThreadPool::Task ThreadPool::take()
{//加锁保护
MutexLockGuard lock(mutex_);
// always use a while-loop, due to spurious wakeup
//如果队列为空并且处于运行的状态
while (queue_.empty() && running_)
{
cond_.wait();//等待
}
Task task;//定义任务变量,Task是一个函数类型
if(!queue_.empty())//有任务到来
{
task = queue_.front();//取出任务
queue_.pop_front();//弹出任务
}
return task;//返回任务
}

http://blog.csdn.net/L979951191/article/details/48089523


EPollPoller(epoll封装类)类对象监听3种类型的事件,一种是由Channel类描述的读写事件,一种是TimeQueue类描述的定时事件(然而也是通过Channel来注册到poller上),还有一种则是自身线程唤醒事件(eventfd)。 
成员变量:
  int epollfd_;   //epoll句柄,以后要监听什么事件注册到这里
typedef std::vector<struct epoll_event> EventList;
EventList events_; //监听到的活动的事件,作为epoll_wait参数
typedef std::map<int, Channel*> ChannelMap;
ChannelMap channels_; //描述符与Channel(事件)的映射,这里的事件,不一定是正在监听的事件(因为有可能是之前监听但后来删掉不过仍留在ChannelMap中的映射)
EventLoop* ownerLoop_;//指向EventLoop的指针。

对文件描述符的封装:

类的成员变量有fd以及对应的callback,该类可以设置fd关心的io事件类型,事件循环中调用epoll返回就绪的fd以及当前活动事件类型,根据当前活动事件类型调用相应的callback.每个fd对象只由一个事件循环eventloop处理

 
 
 
 
 
 
 
http://www.voidcn.com/blog/u010235142/article/p-6071685.html
 
 
 
 
 
 
 
 
 
 
 
 

muduo定时器、多线程模型及epoll的封装的更多相关文章

  1. Muduo 多线程模型对比

    本文主要对比Muduo多线程模型方案8 和方案9 . 方案8:reactor + thread pool ,有一个线程来充当reactor 接受连接分发事件,将要处理的事件分配给thread pool ...

  2. Chrome多线程模型

    为什么使用多线程? Chrome的多线程模型主要解决什么问题? 如何实现该问题的解决? 1. 解决问题 Chrome有很多线程,这是为了保持UI线程(主线程)的高响应度,防止被其他费时的操作阻碍从而影 ...

  3. 第13章 TCP编程(4)_基于自定义协议的多线程模型

    7. 基于自定义协议的多线程模型 (1)服务端编程 ①主线程负责调用accept与客户端连接 ②当接受客户端连接后,创建子线程来服务客户端,以处理多客户端的并发访问. ③服务端接到的客户端信息后,回显 ...

  4. python实现并发服务器实现方式(多线程/多进程/select/epoll)

    python实现并发服务器实现方式(多线程/多进程/select/epoll)   并发服务器开发 并发服务器开发,使得一个服务器可以近乎同一时刻为多个客户端提供服务.实现并发的方式有多种,下面以多进 ...

  5. [源码分析] 分布式任务队列 Celery 多线程模型 之 子进程

    [源码分析] 分布式任务队列 Celery 多线程模型 之 子进程 目录 [源码分析] 分布式任务队列 Celery 多线程模型 之 子进程 0x00 摘要 0x01 前文回顾 1.1 基类作用 1. ...

  6. 追求性能极致:Redis6.0的多线程模型

    Redis系列1:深刻理解高性能Redis的本质 Redis系列2:数据持久化提高可用性 Redis系列3:高可用之主从架构 Redis系列4:高可用之Sentinel(哨兵模式) Redis系列5: ...

  7. 【转载】COM的多线程模型

    原文:COM的多线程模型 COM的多线程模型是COM技术里头最难以理解的部分之一,很多书都有涉及但是都没有很好的讲清楚.很多新人都会在这里觉得很迷惑,google大神能搜到一篇vckbase上的文章, ...

  8. libevent源码阅读笔记(一):libevent对epoll的封装

    title: libevent源码阅读笔记(一):libevent对epoll的封装 最近开始阅读网络库libevent的源码,阅读源码之前,大致看了张亮写的几篇博文(libevent源码深度剖析 h ...

  9. Oracle12c(12.1)中性能优化&amp;功能增强之通过参数THREADED_EXECTION使用多线程模型

    1.   后台 UNIX/Linux系统上,oracle用多进程模型.例如:linux上一个常规安装的数据库会有如下进程列: $ ps -ef | grep [o]ra_ oracle  15356  ...

随机推荐

  1. vue2.0 路由知识一(路由的创建的全过程)

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  2. 【译】x86程序员手册34-9.7错误代码

    9.7 Error Code 错误代码 With exceptions that relate to a specific segment, the processor pushes an error ...

  3. 合并百度影音的离线数据 with python 2.2 bdv格式的更新

    百度影音的bdv格式又有变化. 此次存在2种bdv格式. 格式1:每个文件夹内就一个bdv文件,文件合并后改名avi即可. 格式2:每个文件夹内一个bdv文件作为索引,其他附加guid的文件作为数据. ...

  4. iTOP-4412开发板-LinuxC-继电器模块的测试例程

    平台:iTOP-4412开发板 实现:继电器模块测试例程 继电器的 C 的测试程序,C 测试程序可以在 Android系统,Qt 系统以及最小 linux 系统上运行,文档以 Android 系统上测 ...

  5. BEGIN - 开始一个事务块

    SYNOPSIS BEGIN [ WORK | TRANSACTION ] DESCRIPTION 描述 BEGIN 初始化一个事务块, 也就是说所有 BEGIN 命令后的用户语句都将在一个事务里面执 ...

  6. 微信小程序------微信支付模块

    最近项目涉及到小程序开发:需要进行微信支付模块,接下来通过叙述,记录一下微信小程序中微信支付模块的开发,以便日后翻阅和使用. 学习指南----------微信支付开发文档:https://pay.we ...

  7. Android自动化测试之Monkey

    本来是做Web后端的,来公司实习变成微信小程序前端了,到这周变成Android APP测试人员了,也是微醺啊. 由于对手工测试终究是有些抵触,所有昨天小试了一下不用写代码的自动化压力测试,在此记下我的 ...

  8. vue 选项卡

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <script sr ...

  9. BZOJ 3158 千钧一发 最小割

    分析: 偶数对满足条件2,所有奇数对满足条件1. 如果你能一眼看出这个规律,这道题就完成了一半. 我们只需要将数分为两类,a值为奇数,就从S向这个点连容量为b值的边,a值为偶数,就从这个点向T连容量为 ...

  10. 10 行 Python 代码,批量压缩图片 500 张,简直太强大了

    本文原创并首发于公众号[Python猫],未经授权,请勿转载. 原文地址:https://mp.weixin.qq.com/s/5hpFDgjCpfb0O1Jg-ycACw 熟悉 "Pyth ...