zoukankan      html  css  js  c++  java
  • Libevent参考手册第一章:设置libevent(二)

    4 锁和线程

    编写多线程程序的时候,在多个线程中同时访问同样的数据并不总是安全的。

    libevent的结构体在多线程下通常有三种工作方式:

    ²  某些结构体内在地是单线程的:同时在多个线程中使用它们总是不安全的。

    ²  某些结构体具有可选的锁:可以告知libevent是否需要在多个线程中使用每个对象。

    ²  某些结构体总是锁定的:如果libevent在支持锁的配置下运行,在多个线程中使用它们总是安全的。

    为获取锁,在调用分配需要在多个线程间共享的结构体的libevent函数之前,必须告知libevent使用哪个锁函数。

    如果使用pthreads库,或者使用Windows本地线程代码,那么你是幸运的:已经有设置好的libevent预定义函数能够正确的使用pthreads或者Windows函数。

    接口

    1. #ifdef WIN32  
    2. int evthread_use_windows_threads(void);  
    3. #define EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED  
    4. #endif  
    5. #ifdef _EVENT_HAVE_PTHREADS  
    6. int evthread_use_pthreads(void);  
    7. #define EVTHREAD_USE_PTHREADS_IMPLEMENTED  
    8. #endif  

    这些函数在成功时都返回0,失败时返回-1。

    如果使用不同的线程库,则需要一些额外的工作,必须使用你的线程库来定义函数去实现:

    l  锁

    l  锁定

    l  解锁

    l  分配锁

    l  析构锁

    l  条件变量

    l  创建条件变量

    l  析构条件变量

    l  等待条件变量

    l  触发/广播某条件变量

    l  线程

    l  线程ID检测

    使用evthread_set_lock_callbacks和evthread_set_id_callback接口告知libevent这些函数。

    接口

    1. #define EVTHREAD_WRITE  0x04  
    2. #define EVTHREAD_READ   0x08  
    3. #define EVTHREAD_TRY    0x10  
    4.   
    5. #define EVTHREAD_LOCKTYPE_RECURSIVE 1  
    6. #define EVTHREAD_LOCKTYPE_READWRITE 2  
    7.   
    8. #define EVTHREAD_LOCK_API_VERSION 1  
    9.   
    10. struct evthread_lock_callbacks {  
    11.        int lock_api_version;  
    12.        unsigned supported_locktypes;  
    13.        void *(*alloc)(unsigned locktype);  
    14.        void (*free)(void *lock, unsigned locktype);  
    15.        int (*lock)(unsigned mode, void *lock);  
    16.        int (*unlock)(unsigned mode, void *lock);  
    17. };  
    18.   
    19. int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *);  
    20.   
    21. void evthread_set_id_callback(unsigned long (*id_fn)(void));  
    22.   
    23. struct evthread_condition_callbacks {  
    24.         int condition_api_version;  
    25.         void *(*alloc_condition)(unsigned condtype);  
    26.         void (*free_condition)(void *cond);  
    27.         int (*signal_condition)(void *cond, int broadcast);  
    28.         int (*wait_condition)(void *cond, void *lock,  
    29.             const struct timeval *timeout);  
    30. };  
    31.   
    32. int evthread_set_condition_callbacks(  
    33.         const struct evthread_condition_callbacks *);  

     evthread_lock_callbacks结构体描述的锁回调函数及其能力。对于上述版本,lock_api_version字段必须设置为EVTHREAD_LOCK_API_VERSION。必须设置supported_locktypes字段为EVTHREAD_LOCKTYPE_*常量的组合以描述支持的锁类型(在2.0.4-alpha版本中,EVTHREAD_LOCK_RECURSIVE是必须的,EVTHREAD_LOCK_READWRITE则没有使用)。alloc函数必须返回指定类型的新锁;

    free函数必须释放指定类型锁持有的所有资源;lock函数必须试图以指定模式请求锁定,如果成功则返回0,失败则返回非零;unlock函数必须试图解锁,成功则返回0,否则返回非零。

    可识别的锁类型有:

    0:通常的,不必递归的锁。

    EVTHREAD_LOCKTYPE_RECURSIVE:不会阻塞已经持有它的线程的锁。一旦持有它的线程进行原来锁定次数的解锁,其他线程立刻就可以请求它了。

    EVTHREAD_LOCKTYPE_READWRITE:可以让多个线程同时因为读而持有它,但是任何时刻只有一个线程因为写而持有它。写操作排斥所有读操作。

    可识别的锁模式有:

    EVTHREAD_READ:仅用于读写锁:为读操作请求或者释放锁

    EVTHREAD_WRITE:仅用于读写锁:为写操作请求或者释放锁

    EVTHREAD_TRY:仅用于锁定:仅在可以立刻锁定的时候才请求锁定

    id_fn参数必须是一个函数,它返回一个无符号长整数,标识调用此函数的线程。对于相同线程,这个函数应该总是返回同样的值;而对于同时调用该函数的不同线程,必须返回不同的值。

    evthread_condition_callbacks结构体描述了与条件变量相关的回调函数。对于上述版本,condition_api_version字段必须设置为EVTHREAD_CONDITION_API_VERSION。alloc_condition函数必须返回到新条件变量的指针。它接受0作为其参数。free_condition函数必须释放条件变量持有的存储器和资源。wait_condition函数要求三个参数:一个由alloc_condition分配的条件变量,一个由你提供的evthread_lock_callbacks.alloc函数分配的锁,以及一个可选的超时值。调用本函数时,必须已经持有参数指定的锁;本函数应该释放指定的锁,等待条件变量成为授信状态,或者直到指定的超时时间已经流逝(可选)。wait_condition应该在错误时返回-1,条件变量授信时返回0,超时时返回1。返回之前,函数应该确定其再次持有锁。最后,signal_condition函数应该唤醒等待该条件变量的某个线程(broadcast参数为false时),或者唤醒等待条件变量的所有线程(broadcast参数为true时)。只有在持有与条件变量相关的锁的时候,才能够进行这些操作。

    关于条件变量的更多信息,请查看pthreads的pthread_cond_*函数文档,或者Windows的CONDITION_VARIABLE(Windows Vista新引入的)函数文档。

    示例

    关于使用这些函数的示例,请查看Libevent源代码发布版本中的evthread_pthread.c和evthread_win32.c文件。

    这些函数在<event2/thread.h>中声明,其中大多数在2.0.4-alpha版本中首次出现。2.0.1-alpha到2.0.3-alpha使用较老版本的锁函数。event_use_pthreads函数要求程序链接到event_pthreads库。

    条件变量函数是2.0.7-rc版本新引入的,用于解决某些棘手的死锁问题。

    可以创建禁止锁支持的libevent。这时候已创建的使用上述线程相关函数的程序将不能运行。

    5 调试锁的使用

    为帮助调试锁的使用,libevent有一个可选的“锁调试”特征。这个特征包装了锁调用,以便捕获典型的锁错误,包括:

    l  解并没有真正持有的锁。

    l  重新锁定一个非递归锁。

    如果发生这些错误中的某一个,libevent将给出断言失败并且退出。

    接口

    void evthread_enable_lock_debuging(void);

    注意

    必须在创建或者使用任何锁之前调用这个函数。为安全起见,请在设置完线程函数后立即调用这个函数。

    这个函数是在2.0.4-alpha版本新引入的。

    6 调试事件的使用

    libevent可以检测使用事件时的一些常见错误并且进行报告。这些错误包括:

    l  将未初始化的event结构体当作已经初始化的。

    l  试图重新初始化一个挂起的event结构体。

    跟踪哪些事件已经初始化需要使用额外的内存和处理器时间,所以只应该在真正调试程序的时候才启用调试模式。

    接口

    void event_enable_debug_mode(void);
     

    必须在创建任何event_base之前调用这个函数。

    如果在调试模式下使用大量由event_assign()(而不是event_new())创建的事件,程序可能会耗尽内存,这是因为没有方式可以告知libevent由event_assign()创建的事件不会再被使用了(可以调用event_free()告知由event_new()创建的事件已经无效了)。如果想在调试时避免耗尽内存,可以显式告知libevent由event_assign()创建的这些事件不再被当作已分配的了:

    接口

    void event_debug_unassign(struct event *ev);

    没有启用调试的时候调用event_debug_unassign没有效果。

    示例

    1. #include <event2/event.h>  
    2. #include <event2/event_struct.h>  
    3.   
    4. #include <stdlib.h>  
    5.   
    6. void cb(evutil_socket_t fd, short what, void *ptr)  
    7. {  
    8.     /* We pass 'NULL' as the callback pointer for the heap allocated 
    9.      * event, and we pass the event itself as the callback pointer 
    10.      * for the stack-allocated event. */  
    11.     struct event *ev = ptr;  
    12.   
    13.     if (ev)  
    14.         event_debug_unassign(ev);  
    15. }  
    16.   
    17. /* Here's a simple mainloop that waits until fd1 and fd2 are both 
    18.  * ready to read. */  
    19. void mainloop(evutil_socket_t fd1, evutil_socket_t fd2, int debug_mode)  
    20. {  
    21.     struct event_base *base;  
    22.     struct event event_on_stack, *event_on_heap;  
    23.   
    24.     if (debug_mode)  
    25.        event_enable_debug_mode();  
    26.   
    27.     base = event_base_new();  
    28.   
    29.     event_on_heap = event_new(base, fd1, EV_READ, cb, NULL);  
    30.     event_assign(&event_on_stack, base, fd2, EV_READ, cb, &event_on_stack);  
    31.   
    32.     event_add(event_on_heap, NULL);  
    33.     event_add(&event_on_stack, NULL);  
    34.   
    35.     event_base_dispatch(base);  
    36.   
    37.     event_free(event_on_heap);  
    38.     event_base_free(base);  
    39. }  

    这些调试函数在libevent 2.0.4-alpha版本中加入。

    7 检测libevent的版本

    新版本的libevent会添加特征,移除bug。有时候需要检测libevent的版本,以便:

    l  检测已安装的libevent版本是否可用于创建你的程序。

    l  为调试显示libevent的版本。

    l  检测libevent的版本,以便向用户警告bug,或者提示要做的工作。

    接口

    [c-sharp] view plaincopy
    1. #define LIBEVENT_VERSION_NUMBER 0x02000300  
    2. #define LIBEVENT_VERSION "2.0.3-alpha"  
    3. const char *event_get_version(void);  
    4. ev_uint32_t event_get_version_number(void);  

    宏返回编译时的libevent版本;函数返回运行时的libevent版本。注意:如果动态链接到libevent,这两个版本可能不同。

    可以获取两种格式的libevent版本:用于显示给用户的字符串版本,或者用于数值比较的4字节整数版本。整数格式使用高字节表示主版本,低字节表示副版本,第三字节表示修正版本,最低字节表示发布状态:0表示发布,非零表示某特定发布版本的后续开发序列。

    所以,libevent 2.0.1-alpha发布版本的版本号是[02 00 01 00],或者说0x02000100。2.0.1-alpha和2.0.2-alpha之间的开发版本可能是[02 00 01 08],或者说0x02000108。

    示例:编译时检测

    1. #include <event2/event.h>  
    2.   
    3. #if !defined(LIBEVENT_VERSION_NUMBER) || LIBEVENT_VERSION_NUMBER < 0x02000100  
    4. #error "This version of Libevent is not supported; Get 2.0.1-alpha or later."  
    5. #endif  
    6.   
    7. int  
    8. make_sandwich(void)  
    9. {  
    10.         /* Let's suppose that Libevent 6.0.5 introduces a make-me-a 
    11.            sandwich function. */  
    12. #if LIBEVENT_VERSION_NUMBER >= 0x06000500  
    13.         evutil_make_me_a_sandwich();  
    14.         return 0;  
    15. #else  
    16.         return -1;  
    17. #endif  
    18. }  

    示例:运行时检测

    1. #include <event2/event.h>  
    2. #include <string.h>  
    3.   
    4. int  
    5. check_for_old_version(void)  
    6. {  
    7.     const char *v = event_get_version();  
    8.     /* This is a dumb way to do it, but it is the only thing that works 
    9.        before Libevent 2.0. */  
    10.     if (!strncmp(v, "0.", 2) ||  
    11.         !strncmp(v, "1.1", 3) ||  
    12.         !strncmp(v, "1.2", 3) ||  
    13.         !strncmp(v, "1.3", 3)) {  
    14.   
    15.         printf("Your version of Libevent is very old.  If you run into bugs,"  
    16.                " consider upgrading./n");  
    17.         return -1;  
    18.     } else {  
    19.         printf("Running with Libevent version %s/n", v);  
    20.         return 0;  
    21.     }  
    22. }  
    23.   
    24. int  
    25. check_version_match(void)  
    26. {  
    27.     ev_uint32_t v_compile, v_run;  
    28.     v_compile = LIBEVENT_VERSION_NUMBER;  
    29.     v_run = event_get_version_number();  
    30.     if ((v_compile & 0xffff0000) != (v_run & 0xffff0000)) {  
    31.         printf("Running with a Libevent version (%s) very different from the "  
    32.                "one we were built with (%s)./n", event_get_version(),  
    33.                LIBEVENT_VERSION);  
    34.         return -1;  
    35.     }  
    36.     return 0;  
    37. }  

    本节描述的宏和函数定义在<event2/event.h>中。event_get_version函数首次出现在1.0c版本;其他的首次出现在2.0.1-alpha版本。

  • 相关阅读:
    while for循环
    Python模块
    python内置函数
    【简介】《GM/T 0034-2014 基于SM2密码算法的证书认证系统密码及其相关安全技术规范》
    Markdown的Diagrams
    密码设备管理-对称密钥管理
    TortoiseSVN的简单使用
    Android Studio安装后的设置
    Android Studio升级后,新建Activity后setContentView(R.layout.activity_layout_main);中R变红
    简介Floyd算法
  • 原文地址:https://www.cnblogs.com/chunlinge/p/3463468.html
Copyright © 2011-2022 走看看