libevent定时器是怎么实现的

Posted cpp加油站

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了libevent定时器是怎么实现的相关的知识,希望对你有一定的参考价值。

1. 定时器是怎么实现的

在之前的文章里我们讲过,libevent最后处理都是在event_base_loop调用了相应的dispatch函数,定时器也是在dispatch函数中处理的。

还是以epoll为例,在epoll_dispatch函数有以下一段代码:

 if (tv != NULL) {
  timeout = evutil_tv_to_msec_(tv);
  if (timeout < 0 || timeout > MAX_EPOLL_TIMEOUT_MSEC) {
   /* Linux kernels can wait forever if the timeout is
    * too big; see comment on MAX_EPOLL_TIMEOUT_MSEC. */
   timeout = MAX_EPOLL_TIMEOUT_MSEC;
  }
 }

 epoll_apply_changes(base);
 event_changelist_remove_all_(&base->changelist, base);

 EVBASE_RELEASE_LOCK(base, th_base_lock);

 //epoll_wait的最后一个参数即为超时时间
 res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);

 EVBASE_ACQUIRE_LOCK(base, th_base_lock);

 if (res == -1) {
  if (errno != EINTR) {
   event_warn("epoll_wait");
   return (-1);
  }

  return (0);
 }

从上面代码可以看出,是通过epoll_wait的超时机制来实现定时器的,这样我们就可以知道,其实定时器就是利用了select和epoll_wait等这些系统函数的超时机制,才实现的定时器。

总的来讲,定时器就是在事件主循环中,等待网络调用超时,当超时以后,将任务写入队列,然后处理队列,调用回调函数,这样就实现了定时器。

2. 定时器代码实现

看libevent源代码中例子:

#include <sys/types.h>
#include <event2/event-config.h>
#include <sys/stat.h>
#include <time.h>
#ifdef EVENT__HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <event2/event.h>
#include <event2/event_struct.h>
#include <event2/util.h>

struct timeval lasttime;

int event_is_persistent;

static void
timeout_cb(evutil_socket_t fd, short event, void *arg)
{
 struct timeval newtime, difference;
 struct event *timeout = (struct event*)arg;
 double elapsed;

 evutil_gettimeofday(&newtime, NULL);
 evutil_timersub(&newtime, &lasttime, &difference);
 elapsed = difference.tv_sec +
     (difference.tv_usec / 1.0e6);

 printf("timeout_cb called at %d: %.3f seconds elapsed.\\n",
     (int)newtime.tv_sec, elapsed);
 lasttime = newtime;

 if (! event_is_persistent) {
  struct timeval tv;
  evutil_timerclear(&tv);
  tv.tv_sec = 2;
  event_add(timeout, &tv);
 }
}

int
main(int argc, char **argv)
{
 struct event timeout;
 struct timeval tv;
 struct event_base *base;
 int flags;

 if (argc == 2 && !strcmp(argv[1], "-p")) {
  event_is_persistent = 1;
  flags = EV_PERSIST;
 } else {
  event_is_persistent = 0;
  flags = 0;
 }

 /* Initalize the event library */
 base = event_base_new();

 /* Initalize one event */
 event_assign(&timeout, base, -1, flags, timeout_cb, (void*) &timeout);

 evutil_timerclear(&tv);
 tv.tv_sec = 3;
 event_add(&timeout, &tv);

 evutil_gettimeofday(&lasttime, NULL);

 event_base_dispatch(base);

 return (0);
}

实现三秒调用一次回调函数,执行结果如下:

timeout_cb called at 1535528104: 3.001 seconds elapsed.
timeout_cb called at 1535528107: 3.000 seconds elapsed.
timeout_cb called at 1535528110: 3.001 seconds elapsed.
timeout_cb called at 1535528113: 2.999 seconds elapsed.
timeout_cb called at 1535528116: 3.000 seconds elapsed.
timeout_cb called at 1535528119: 3.002 seconds elapsed.
timeout_cb called at 1535528122: 2.999 seconds elapsed.
timeout_cb called at 1535528125: 3.001 seconds elapsed.
timeout_cb called at 1535528128: 3.000 seconds elapsed.
timeout_cb called at 1535528131: 3.000 seconds elapsed.
timeout_cb called at 1535528134: 2.999 seconds elapsed.
timeout_cb called at 1535528137: 3.000 seconds elapsed.
timeout_cb called at 1535528140: 3.000 seconds elapsed.
timeout_cb called at 1535528143: 3.000 seconds elapsed.
timeout_cb called at 1535528146: 3.000 seconds elapsed.
timeout_cb called at 1535528149: 3.002 seconds elapsed.

以上是关于libevent定时器是怎么实现的的主要内容,如果未能解决你的问题,请参考以下文章

libevent总结(上)

定时器管理:nginx的红黑树和libevent的堆

libevent总结(下)

libevent总结学习

libevent使用

Libevent的IO复用技术和定时事件原理