• Libevent:3创建event_base


             在使用Libevent函数之前,需要分配一个或多个event_base结构。每一个event_base都持有一个events的集合,并且可以检测那些events是激活的。

             如果设置event_base可以使用锁,那event_base是可以安全的在多线程环境中使用,但是,只能在单个的线程中进行event_baseloop。如果希望在多线程中进行IO轮询,那就需要每个线程都有一个event_base(未来版本的Libevent可能会支持多线程支持event_base的loop)

             每一个event_base都会有一个“后台方法”,用来检测哪一个events准备好了。主要包含下列方法:select、poll、epoll、kqueue、devpoll、evport、win32.

             用户可以通过环境变量禁止某些特定的后台方法。比如,如果想要禁止kqueue后台,可以设置环境变量EVENT_NOKQUEUE。如果想在程序中关闭后台,可以参见下面的event_config_avoid_method()函数。

     

    一:设置默认event_base

             event_base_new()函数分配并返回一个默认设置属性的event_base。它检查环境变量,并返回一个指向新的event_base的指针。如果发生错误,则返回NULL。

    struct  event_base *event_base_new(void);

             它会挑选操作系统支持的最快的后台方法。对于大多数程序来说,使用这个接口基本上就足够了。该接口在<event2/event.h>中声明。

     

    二:设置复杂event_base

             如果希望对event_base有更多的控制,那就需要event_config结构。event_config是一个不透明的结构,包含可以设置的event_base信息。可以通过传递event_config给函数event_base_new_with_config()对event_base进行设置。

     

    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,可以通过event_config_new来分配新的event_config结构。然后调用其他函数,根据需要设置event_config。最后,调用event_base_new_with_config返回一个新的event_base。完成之后,使用event_config_free释放event_config结构。

     

    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(),可以让Libevent避免使用特定后端方法。调用event_config_require_feature(),禁止Libevent使用任何无法提供一组特性的后端方法。调用event_config_set_flag(),可以在创建event_base时,设置一些运行时标志。

             event_config_require_features支持的特性有:

             EV_FEATURE_ET:需要支持边沿触发的后端方法

             EV_FEATURE_O1:需要这样的后端方法,它的添加、删除event,或者确定哪个event变为激活等操作,都是O(1)的。

             EV_FEATURE_FDS:需要可以支持任意文件描述符类型的后台方法,而不仅仅支持socket。

     

             event_config_set_flag()支持的选项有:

             EVENT_BASE_FLAG_NOLOCK:不为event_base分配锁。尽管这样做可以节省一点在event_base上加锁和解锁的时间,但是这会导致多线程环境下,无法安全的使用event_base。

             EVENT_BASE_FLAG_IGNORE_ENV:在挑选使用哪个后台方法时,不检查EVENT_*环境变量,使用该标志之前要仔细考虑,因为他使得调试应用程序和Libevent之间的交互变得困难。

             EVENT_BASE_FLAG_STARTUP_IOCP:仅用于Windows,该标志使得Libevent在启动时就使能必要的IOCP调度逻辑,而不是按需使能。

             EVENT_BASE_FLAG_NO_CACHE_TIME:不是每次event loop准备运行超时回调函数时,检测当前时间,而是每次调用超时回调函数之后在检测。这样做会消耗额外的CPU,所以要小心。

             EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST:告诉Libevent,如果使用的是epoll后端方法,那可以安全的使用基于changelist的更快的后端方法。如果在调用后端调度函数的中间,同样的描述符的状态多次改变的话,epoll-changelist可以避免不必要的系统调用。但是如果传递给Libevent的描述符fd是dup复制而来的话,那么会触发一个内核bug。该标志只有在使用epoll后端时有效。设置EVENT_EPOLL_USE_CHANGELIST环境变量,同样可以开启epoll-changelist选项。

             EVENT_BASE_FLAG_PRECISE_TIMER:默认情况下,Libevent使用系统提供的最快的定时机制,如果存在较慢的定时机制,但是它可以提供更精细的定时精度,那么该标志可以使Libevent使用这种定时机制。如果操作系统没有这样的定时机制的话,则该标志是无效的。

     

    上述操作event_config的函数在成功是返回0,失败是返回-1.

             注意:设置event_config请求操作系统没有提供的后台方法是很容易的。比如,对于Libevent2.0.1-alpha来说,Windows没有O(1)的后端方法,而且Linux也不能提供EV_FEATURE_FDS EV_FEATURE_O1的后端方法。如果配置的Libevent无法满足的话,那么event_base_new_with_config()会返回NULL

     

    int  event_config_set_num_cpus_hint(struct event_config *cfg, int cpus)

             该接口目前仅在Windows上使用IOCP时才支持,未来版本其他平台上也许也会支持。调用该接口,使得由event_config配置生成的event_base可以在多线程时更加有效的使用给定的cpu个数。注意,注意这仅仅是一个提示:event_base使用的CPU可能比你选择的要少。

     

    intevent_config_set_max_dispatch_interval(struct event_config *cfg,

        const struct timeval *max_interval, intmax_callbacks,

        int min_priority);

             该接口可以防止优先级反转。这是通过在检查更高优先级的events之前,限制有多少低优先级的event回调可以被调用而实现的。如果max_interval非NULL,在每次回调之后,event loop都会检查时间,而且经过max_interval时间之后,重新扫描更高优先级的events。如果max_callbacks非0,那么max_callback个回调被调用之后,event loop会检查更多的events。这些规则针对min_priority以及以上的events有效。

             该接口设置在经过多长时间,或者(以及)经过多少个回调之后,重新检查新的events。默认情况下,event_base在检查新events之前,会运行尽可能多的被激活的高优先级events。如果设置了max_interval之后,那么每次回调之后都会检查时间,保证检查新events的最长时间间隔就是max_interval。如果设置了大于0max_callbacks,那么在检查新的events之前,最多运行max_callbacks个回调。

             该接口可以减少高优先级events的延迟。而且在有多个低优先级events时,避免出现优先级倒置。但是这是以降低吞吐量为代价的。

             max_interval :经过该时间之后,Libevent应该停止运行回调,而检查新的events,如果该参数为NULL,则无此时间限制。

             max_callbacks:经过max_callbacks个回调之后,Libevent应该停止运行回调,并且检查更多的events。如果该参数为-1,则无此个数限制。

             min_priority:低于该优先级的events,max_interval和max_callbacks将不会生效。如果该值为0,则所有优先级的events都会生效。如果置为1,则针对优先级为1,以及更高优先级的events才会生效。

    struct event_config *cfg;

    struct event_base *base;

    int i;

     

    /* My program wants to use edge-triggered events if atall possible.  So

       I'll try to geta base twice: Once insisting on edge-triggered IO, and

       once not. */

    for (i=0; i<2; ++i) {

        cfg =event_config_new();

     

        /* I don't likeselect. */

       event_config_avoid_method(cfg, "select");

     

       if (i == 0)

           event_config_require_features(cfg, EV_FEATURE_ET);

     

        base =event_base_new_with_config(cfg);

       event_config_free(cfg);

       if (base)

           break;

     

        /* If we gethere, event_base_new_with_config() returned NULL.  If

           this is thefirst time around the loop, we'll try again without

           settingEV_FEATURE_ET.  If this is the secondtime around the

           loop, we'llgive up. */

    }

     

     

    struct event_config *cfg;

    struct event_base *base;

     

    cfg = event_config_new();

    if (!cfg)

       /* Handle error*/;

     

    /* I'm going to have events running at twopriorities.  I expect that

       some of mypriority-1 events are going to have pretty slow callbacks,

       so I don't wantmore than 100 msec to elapse (or 5 callbacks) before

       checking forpriority-0 events. */

    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);

             以上所有的函数和类型都在<event2/event.h>中声明。

     

    三:检测event_base的后端方法

             有时你可能需要知道一个event_base实际具有哪些特性,或者使用哪一个后端方法。

     

    const char **event_get_supported_methods(void);

             event_get_supported_methods()返回一个指向数组的指针。该数组包含所有当前Libevent所支持的后端方法的名字,该数组最后一个元素为NULL。

    int i;

    const char **methods =event_get_supported_methods();

    printf("Starting Libevent%s.  Available methods are: ",

        event_get_version());

    for (i=0; methods[i] != NULL;++i) {

        printf("    %s ", methods[i]);

    }

             注意:该函数返回的,是Libevent编译时支持的一系列后端方法,有可能Libevent在尝试运行时,操作系统实际上并不支持所有的方法。比如,如果你使用OSX系统的话,那么kqueue可能会有太多的漏洞而无法使用。

     

    const char *event_base_get_method(conststruct event_base *base);

    enum event_method_featureevent_base_get_features(const struct event_base *base);

             event_base_get_method()方法返回的是event_base实际使用的后端方法名称。event_base_get_features()返回它所支持的特性的掩码组合。

    struct event_base *base;

    enum event_method_feature f;

     

    base = event_base_new();

    if (!base) {

        puts("Couldn't get anevent_base!");

    } else {

        printf("Using Libevent with backendmethod %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("");

    }

             这些方法定义在文件<event2/event.h>中。

     

    四:释放event_base

             当一个event_base完成任务的时候,就可以调用event_base_free释放它。

    void event_base_free(structevent_base *base);

             注意,该函数并不释放任何与event_base所关联的events,也不会关闭sockets,更不会释放他们的指针。该方法在<event2/event.h>中定义。

     

    五:设置event_base 的优先级

             Libevent支持在event上设置多个优先级。默认情况下,一个event_base仅有一个优先级。可以通过函数event_base_priority_init来设置event_base的优先级等级数。

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

             该函数成功时返回0,失败是返回-1。参数base是需要修改的event_base, n_priorities是支持的优先级等级数。该参数至少为1. 调用该函数之后,新的events的优先级等级就是从0(最高)到n_priorities -1(最低)。

             常数EVENT_MAX_PRIORITIE,是n_priorities的值的上限。如果使用比该常数还要大的数调用该函数,就会发生错误。

             注意,必须在任何events变为激活时调用该函数。最好是创建event_base之后就立即调用它。

     

             调用event_base_getnpriorities(),可以得到当前event_base所支持的优先级等级个数。

    int event_base_get_npriorities(structevent_base *base);

             该方法返回在event_base上配置的优先级等级数。所以如果该函数返回3,那么支持的优先级就是0,1和2.

     

             默认情况下,所有关联到event_base的events的初始优先级都是n_priorities/ 2。

             函数event_base_priority_init,在<event2/event.h>中定义。

     

    六:fork之后,重新初始化event_base

             并不是所有event后端,在fork之后都能正确工作。所以,如果你的程序使用fork(或其他系统调用)开始一个新进程,如果在fork之后,还想继续使用event_base,那么需要对其进行重新初始化。

    int event_reinit(structevent_base *base);

             该方法在成功是返回0,失败是返回-1.

    struct event_base *base =event_base_new();

     

    /* ... add some events to theevent_base ... */

     

    if (fork()) {

        /* In parent */

        continue_running_parent(base); /*...*/

    } else {

        /* In child */

        event_reinit(base);

        continue_running_child(base); /*...*/

    }

             该方法在<event2/event.h>中定义。

     

    七:过时的event_base方法

             老版本的Libevent严重依赖于“当前”event_base的概念。“当前”event_base是指所有线程都共享的全局设置。如果你忘了指定需要哪一个event_base,那会得到“当前”的那一个。因为event_base不是线程安全的,所以这样很容易出错。

             event_base_new的老版本是:

    struct event_base*event_init(void);

             该方法类似于event_base_new,将当前event_base设置为分配的event_base。没有任何其他方法可以改变当前base。

           本文描述的函数有一些用于操作当前event_base的变体,这些函数与新版本函数的行为类似,只是它们没有event_base参数。

    Current function

    Obsolete current-base version

    event_base_priority_init()

    event_priority_init()

    event_base_get_method()

    event_get_method()

     

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

  • 相关阅读:
    Appium环境搭建+cordova
    Appium简单测试用例
    appium常用方法整理
    JAVA+Maven+TestNG搭建接口测试框架及实例
    stm32填坑之旅一
    再度分(tu)析(cao)Egret这个年轻人
    Egret的若干局限
    egret随笔-egret浅入浅出
    一步一步实现AS3拖放组件
    react-native学习笔记——ViewStack组件
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7247260.html
Copyright © 2020-2023  润新知