ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

muduo库net源码分析三(定时器)

2022-02-21 14:30:00  阅读:152  来源: 互联网

标签:muduo const int 定时器 timer 源码 timerfd net include


让EventLoop 能够处理定时器事件

定时函数

用于让程序等待一段时间或安排计划任务:

sleep

alarm usleep

nanosleep

clock_nanosleep

getitimer / setitimer

timer_create / timer_settime / timer_gettime / timer_delete

timerfd_create / timerfd_gettime / timerfd_settime 选择这种方法。

为什么选择timerfd_create?

sleep / alarm / usleep 在实现时有可能用了信号 SIGALRM,在多线程程序中处理信号是个相当麻烦的事情,应当尽量避免 。

nanosleep 和 clock_nanosleep 是线程安全的,但是在非阻塞网络编程中,绝对不能用让线程挂起的方式来等待一段时间,程序会失去响应。正确的做法是注册一个时间回调函数。

getitimer 和 timer_create 也是用信号来 deliver 超时,在多线程程序中也会有麻烦。

timer_create 可以指定信号的接收方是进程还是线程,算是一个进步,不过在信号处理函数(signal handler)能做的事情实在很受限。

timerfd_create 把时间变成了一个文件描述符,该“文件”在定时器超时的那一刻变得可读,这样就能很方便地融入到 select/poll 框架中,用统一的方式来处理 IO 事件和超时事件,这也正是 Reactor 模式的长处。

信号转换成文件描述符来处理

如果要处理信号的话,也可以让信号转换成文件描述符来处理,signalfd

#include <sys/timerfd.h>

int timerfd_create(int clockid, int flags);

int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);

int timerfd_gettime(int fd, struct itimerspec *curr_value)

示例:

#include <muduo/net/Channel.h>
#include <muduo/net/EventLoop.h>
#include <boost/bind.hpp>
#include <stdio.h>
#include <sys/timerfd.h>

using namespace muduo;
using namespace muduo::net;

EventLoop* g_loop;
int timerfd;

void timeout(Timestamp receiveTime)
{
	printf("Timeout!\n");
	uint64_t howmany;
	::read(timerfd, &howmany, sizeof howmany); // 这里一定要读走,不然处于高电平状态一直触发
	g_loop->quit();
}

int main(void)
{
	EventLoop loop;
	g_loop = &loop;

	timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
	Channel channel(&loop, timerfd);
	channel.setReadCallback(boost::bind(timeout, _1));
	channel.enableReading();

	struct itimerspec howlong;
	bzero(&howlong, sizeof howlong);
	howlong.it_value.tv_sec = 1;
	::timerfd_settime(timerfd, 0, &howlong, NULL);

	loop.loop();

	::close(timerfd);
}

muduo 定时器的实现

muduo 定时器由三个类实现,TimerId、Timer、TimerQueue,用户只能看到第一个类,其它两个都是内部实现细节。

timer 定时器的高层次抽象,并没有实际的定时器函数

#ifndef MUDUO_NET_TIMER_H
#define MUDUO_NET_TIMER_H

#include <boost/noncopyable.hpp>

#include <muduo/base/Atomic.h>
#include <muduo/base/Timestamp.h>
#include <muduo/net/Callbacks.h>

namespace muduo
{
namespace net
{
///
/// Internal class for timer event.
///
class Timer : boost::noncopyable
{
 public:
  Timer(const TimerCallback& cb, Timestamp when, double interval)
    : callback_(cb),
      expiration_(when),
      interval_(interval),
      repeat_(interval > 0.0),
      sequence_(s_numCreated_.incrementAndGet())
  { }

  void run() const
  {
    callback_();
  }

  Timestamp expiration() const  { return expiration_; }
  bool repeat() const { return repeat_; }
  int64_t sequence() const { return sequence_; }

  void restart(Timestamp now);

  static int64_t numCreated() { return s_numCreated_.get(); }

 private:
  const TimerCallback callback_;		// 定时器回调函数
  Timestamp expiration_;				// 下一次的超时时刻
  const double interval_;				// 超时时间间隔,如果是一次性定时器,该值为0
  const bool repeat_;					// 是否重复
  const int64_t sequence_;				// 定时器序号

  static AtomicInt64 s_numCreated_;		// 定时器计数,当前已经创建的定时器数量
};
}
}
#endif  // MUDUO_NET_TIMER_H
#include <muduo/net/Timer.h>

using namespace muduo;
using namespace muduo::net;

AtomicInt64 Timer::s_numCreated_;

void Timer::restart(Timestamp now)
{
  if (repeat_)
  {
    // 重新计算下一个超时时刻
    expiration_ = addTime(now, interval_);
  }
  else
  {
    expiration_ = Timestamp::invalid();
  }
}

Muduo类之间的关系

TimerQueue的接口很简单,只有两个函数addTimer和cancel。调用addTimer 返回timerld。添加定时器, timerld 是外部类。调用cancel 传递timerld 取消定时器。实际使用也不用TimerQueue的方法,使用的时候用EventLoop提供的函数。TimerQueue 会调用实际的定时器函数

EventLoop

runAt        在某个时刻运行定时器

runAfter        过一段时间运行定时器

runEvery        每隔一段时间运行定时器

cancel        取消定时器

TimerQueue数据结构的选择,能快速根据当前时间找到已到期的定时器,也要高效的添加和删除Timer,因而可以用二叉搜索树,用map或者set

typedef std::pair<Timestamp, Timer*> Entry;

typedef std::set<Entry> TimerList;

RVO优化

#include <iostream>

using namespace std;

struct Foo   
{   
	Foo() { cout << "Foo ctor" << endl; }
	Foo(const Foo&) { cout << "Foo copy ctor" << endl; }
	void operator=(const Foo&) { cout << "Foo operator=" << endl; }
	~Foo() { cout << "Foo dtor" << endl; }
};  

Foo make_foo()   
{   
	Foo f;
	return f; // RVO 优化没有执行拷贝构造
	//return Foo();  
}
  
int main(void)
{
	make_foo();
	return 0;
}

标签:muduo,const,int,定时器,timer,源码,timerfd,net,include
来源: https://blog.csdn.net/sinat_28697845/article/details/123040554

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有