libevent简介[翻译]5 获得一个event_base

http://www.wangafu.net/~nickm/libevent-book/Ref2_eventbase.html

创建一个event_base

在使用任何libevent函数之前,需要先创建一个或是多个event_base。每一个event_base都包含一个事件集合,它可以准确的通知哪一个事件到达了。

如果event_base使用了锁,那么在多线程下调用就是安全的。它的事件循环只能在一个线程中使用,如果想在多个线程中遍历IO,就需要为每一个线程创建一个event_base

将来可能会提供在多个线程下使用的event_bases

每个event_base都有一个解决方案,后端决定使用哪一个,现有的方案有:

  • select
  • poll
  • epoll
  • kqueue
  • devpoll
  • evport
  • win32

可以通过环境变量禁用其中的功能,如果想关闭kqueue,可以设置EVENT_NOKQUEUE环境变量。如果想从程序中禁用某个方案,可以调用event_config_avoid_method()

设置默认event_base

event_base_new()申请并返回一个新的event base。如果出错返回NULL

接口

struct event_base *event_base_new(void);

设置一个复杂的event_base

如果想自定义一些属性,需要调用event_configevent_config是一个不透明的结构体,保存着event_base的首选项信息。使用event_base_new_with_config()创建event_base,创建的时候可以把event_config传递进去。

接口

struct event_config *event_config_new(void);
struct event_base *event_base_new_with_config(const struct event_config *cfg);
void event_config_free(struct event_config *cfg);

先创建一个配置,然后把这个配置传递进去创建一个event_base,创建完成后可以把这个配置结构体删除了。

接口

int event_config_avoid_method(struct event_config *cfg, const char *method);

enum event_method_feature {
    EV_FEATURE_ET = 0x01,
    EV_FEATURE_O1 = 0x02,
    EV_FEATURE_FDS = 0x04,
};
int event_config_require_features(struct event_config *cfg,
                                  enum event_method_feature feature);

enum event_base_config_flag {
    EVENT_BASE_FLAG_NOLOCK = 0x01,
    EVENT_BASE_FLAG_IGNORE_ENV = 0x02,
    EVENT_BASE_FLAG_STARTUP_IOCP = 0x04,
    EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08,
    EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10,
    EVENT_BASE_FLAG_PRECISE_TIMER = 0x20
};
int event_config_set_flag(struct event_config *cfg,
    enum event_base_config_flag flag);

调用event_config_avoid_method()可以禁用一些功能。event_config_require_feature()设置我们使用的功能。event_config_set_flag()设置多个运行时的标识

event_config_require_features()可以使用的参数有:

EV_FEATURE_ET

  • 使用边界触发

EV_FEATURE_O1

  • 添加、删除或是激活一个event是O(1)的操作

EV_FEATURE_FDS

  • 可以使用任意的文件描述符,而不仅仅是socket

对于event_config_set_flag()可以使用的参数有:

EVENT_BASE_FLAG_NOLOCK

  • 对于event_base的操作,不实用锁,这样可以节省一点点时间,但是在多线程下工作会有问题

EVENT_BASE_FLAG_IGNORE_ENV

  • 禁止使用EVENT_*所有的环境变量。谨慎使用这个标识,如果出现问题,很难调试。

EVENT_BASE_FLAG_STARTUP_IOCP

  • 启动时就使用IOCP,这个仅仅在Windows下有效。

EVENT_BASE_FLAG_NO_CACHE_TIME

  • 把默认的在每次事件循环超时的时候检查当前时间,替换成不是整个循环,而是每个超时回调之后都检查当前时间,这样会大大增加CPU的消耗,带来的是更精确的时钟。慎重考虑。

EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST

  • 这个很明显是针对epoll的,就是如果使用epoll,那么就可以使用changelist。因为changelist可以在同一个fd多次被修改的情况下避免不必要的系统调用;同样,如果如果你传入了通过dup()克隆或是它自己的变体的fd给libevent,那么也会导致内核错误。如果不是epoll模型,这个标识是没有作用的。同样可以通过EVENT_EPOLL_USE_CHANGELIST环境变量打开changelist。

EVENT_BASE_FLAG_PRECISE_TIMER

  • libevent在不同平台下都会使用该系统提供的更快的时钟机制,但是如果该系统上有稍微慢一点的但是更精确的时钟机制,可以通过这个参数设置使用。但是如果系统上没有这种慢一点但是更精确的时钟机制,这个标识不会有任何作用。

上面的这些针对event_config的函数调用返回值都是:0-成功 -1-失败

注意

我们可以很容易的设置event_config,不管底层系统是否支持。比如在Libevent 2.0.1-alpha这个版本上,在Windows下没有O(1)的实现,在Linux下没有EV_FEATURE_FDSEV_FEATURE_O1,如果你设置了libevent无法实现的标识,event_base_new_with_config()就会返回NULL

接口

int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus)

这个函数目前只在Windows下有效,将来可能在其他平台上也可用。调用这个函数就是设置event_config使得event_base在多线程下可以有更好的性能。需要注意的是,最终event_base使用的CPU个数可能比我们设置的多一些,也可能少一些。

接口

int event_config_set_max_dispatch_interval(struct event_config *cfg,
    const struct timeval *max_interval, int max_callbacks,
    int min_priority);

在libevent中有事件优先级,这个函数的作用就是打破事件优先级,让libevent在高优先级的事件处理之前可以处理多少个低优先级的事件,因为如果高优先级的事件一直很多,处理不完,就会导致低优先级的事件永远无法处理。如果max_interval传入的不是NULL,事件循环会在每次回调后检查时钟,当max_interval超时后就重新搜索高优先级的事件。如果max_callbacks是非负的,事件循环还会在调用完所有max_callbacks回调后再调用更多其他事件。这个规则适用于任何min_priority或更高等级的事件。

示例:使用边界触发

struct event_config *cfg;
struct event_base *base;
int i;

/* My program wants to use edge-triggered events if at all possible.  So
   I'll try to get a base twice: Once insisting on edge-triggered IO, and
   once not. */
for (i=0; i<2; ++i) {
    //创建一个cfg
    cfg = event_config_new();

    /* I don't like select. */
    //屏蔽使用select方法
    event_config_avoid_method(cfg, "select");

    if (i == 0)
        //使用ET边界触发
        event_config_require_features(cfg, EV_FEATURE_ET);

    //用上面的cfg创建一个event base
    base = event_base_new_with_config(cfg);

    //释放cfg
    event_config_free(cfg);
    if (base)
        break;

    /* If we get here, event_base_new_with_config() returned NULL.  If
       this is the first time around the loop, we'll try again without
       setting EV_FEATURE_ET.  If this is the second time around the
       loop, we'll give up. */
}

示例:禁止优先级反转

struct event_config *cfg;
struct event_base *base;

cfg = event_config_new();
if (!cfg)
   /* Handle error */;

/* 这里有两个优先级的event在跑。由于优先级1(也就是优先级高)的事件运行的慢,
   所以希望优先级0(也就是优先级低)更多的调用一些。
   这里设置的就是最多100毫秒或是回调5次之后,肯定调用一次优先级0的事件 */
struct timeval msec_100 = { 0, 100*1000 };
event_config_set_max_dispatch_interval(cfg, &msec_100, 5, 1);

base = event_base_new_with_config(cfg);
if (!base)
   /* Handle error */;

event_base_priority_init(base, 2);

检测event_base底层有效的模型

有时候我们想知道event_base底层有哪些模型可用

接口

const char **event_get_supported_methods(void);

这个函数会返回一个数组,里面列出了可用的模型。最后一个指向NULL表示结束。

示例

int i;
const char **methods = event_get_supported_methods();
printf("Starting Libevent %s.  Available methods are:\n",
    event_get_version());
for (i=0; methods[i] != NULL; ++i) {
    printf("    %s\n", methods[i]);
}

接口

const char *event_base_get_method(const struct event_base *base);
enum event_method_feature event_base_get_features(const struct event_base *base);

event_base_get_method获取当前在用的模型。event_base_get_features获取将来可能支持的模型

示例

struct event_base *base;
enum event_method_feature f;

base = event_base_new();
if (!base) {
    puts("Couldn't get an event_base!");
} else {
    printf("Using Libevent with backend method %s.",
        event_base_get_method(base));
    f = event_base_get_features(base);
    if ((f & EV_FEATURE_ET))
        printf("  Edge-triggered events are supported.");
    if ((f & EV_FEATURE_O1))
        printf("  O(1) event notification is supported.");
    if ((f & EV_FEATURE_FDS))
        printf("  All FD types are supported.");
    puts("");
}

释放event_base

当我们使用完后需要释放event_base的资源

void event_base_free(struct event_base *base);

需要注意,这里仅仅是释放event_base,并不会释放任何与它关联的变量,比如socket,或是一些指针。

event_base设置优先级

libevent支持为event设置多个优先级,但是event_base只能设置一个

int event_base_priority_init(struct event_base *base, int n_priorities);

成功返回0,失败返回-1。event_base的n_priorities必须是1,而新创建的event可以是0,也可以是-1

EVENT_MAX_PRIORITIES定义了最高优先级,如果设置过高,会报错

在调用任何event之前设置它的优先级,最好是在创建完成后就设置好。

可以调用int event_base_get_npriorities(struct event_base *base);获取当前event的优先级。如果返回值是3,就表示0,1,2三个优先级是可用的。

一般情况下,新创建的event的优先级都设置为与它关联的event_base的二分之一,n_priorities / 2

fork()之后重新初始化event_base

fork()之后event_base的有些信息并没有完全清理,所以需要初始化

int event_reinit(struct event_base *base);

示例

struct event_base *base = event_base_new();

/* ... add some events to the event_base ... */

if (fork()) {
    /* In parent */
    continue_running_parent(base); /*...*/
} else {
    /* In child */
    event_reinit(base);
    continue_running_child(base); /*...*/
}

废弃的event_base函数

以前版本的libevent很大程度上,在实现是想上,与当前的event_base一样。当前的event_base是一个全局共享的资源。如果没有特殊定义,你是用的是当前的event_base,记住,event_base是线程不安全的,不要使用的时候出错。

建议使用struct event_base *event_init(void);来代替event_base_new()

Current function Obsolete current-base version
event_base_priority_init() event_priority_init()
---- ----
event_base_get_method() event_get_method()
posted @ 2020-06-11 16:39  秋来叶黄  阅读(290)  评论(0编辑  收藏  举报