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

2022/2/21 17:29:17

本文主要是介绍muduo库net源码分析三(定时器),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

让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库net源码分析三(定时器)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程