4 锁和线程windows
编写多线程程序的时候,在多个线程中同时访问一样的数据并不老是安全的。api
libevent的结构体在多线程下一般有三种工做方式:安全
² 某些结构体内在地是单线程的:同时在多个线程中使用它们老是不安全的。多线程
² 某些结构体具备可选的锁:能够告知libevent是否须要在多个线程中使用每一个对象。socket
² 某些结构体老是锁定的:若是libevent在支持锁的配置下运行,在多个线程中使用它们老是安全的。ide
为获取锁,在调用分配须要在多个线程间共享的结构体的libevent函数以前,必须告知libevent使用哪一个锁函数。函数
若是使用pthreads库,或者使用Windows本地线程代码,那么你是幸运的:已经有设置好的libevent预约义函数可以正确的使用pthreads或者Windows函数。oop
这些函数在成功时都返回0,失败时返回-1。ui
若是使用不一样的线程库,则须要一些额外的工做,必须使用你的线程库来定义函数去实现:spa
l 锁
l 锁定
l 解锁
l 分配锁
l 析构锁
l 条件变量
l 建立条件变量
l 析构条件变量
l 等待条件变量
l 触发/广播某条件变量
l 线程
l 线程ID检测
使用evthread_set_lock_callbacks和evthread_set_id_callback接口告知libevent这些函数。
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。这时候已建立的使用上述线程相关函数的程序将不能运行。
为帮助调试锁的使用,libevent有一个可选的“锁调试”特征。这个特征包装了锁调用,以便捕获典型的锁错误,包括:
l 解并无真正持有的锁。
l 从新锁定一个非递归锁。
若是发生这些错误中的某一个,libevent将给出断言失败而且退出。
void evthread_enable_lock_debuging(void);
必须在建立或者使用任何锁以前调用这个函数。为安全起见,请在设置完线程函数后当即调用这个函数。
这个函数是在2.0.4-alpha版本新引入的。
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没有效果。
这些调试函数在libevent 2.0.4-alpha版本中加入。
新版本的libevent会添加特征,移除bug。有时候须要检测libevent的版本,以便:
l 检测已安装的libevent版本是否可用于建立你的程序。
l 为调试显示libevent的版本。
l 检测libevent的版本,以便向用户警告bug,或者提示要作的工做。
宏返回编译时的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。
本节描述的宏和函数定义在<event2/event.h>中。event_get_version函数首次出如今1.0c版本;其余的首次出如今2.0.1-alpha版本。