Libevent参考手册:设置libevent(二)
2014-08-17 01:17
357 查看
4锁和线程
编写多线程程序的时候,在多个线程中同时访问同样的数据并不总是安全的。
libevent的结构体在多线程下通常有三种工作方式:
²某些结构体内在地是单线程的:同时在多个线程中使用它们总是不安全的。
²某些结构体具有可选的锁:可以告知libevent是否需要在多个线程中使用每个对象。
²某些结构体总是锁定的:如果libevent在支持锁的配置下运行,在多个线程中使用它们总是安全的。
为获取锁,在调用分配需要在多个线程间共享的结构体的libevent函数之前,必须告知libevent使用哪个锁函数。
如果使用pthreads库,或者使用Windows本地线程代码,那么你是幸运的:已经有设置好的libevent预定义函数能够正确的使用pthreads或者Windows函数。
这些函数在成功时都返回0,失败时返回-1。
如果使用不同的线程库,则需要一些额外的工作,必须使用你的线程库来定义函数去实现:
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(WindowsVista新引入的)函数文档。
关于使用这些函数的示例,请查看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将给出断言失败并且退出。
必须在创建或者使用任何锁之前调用这个函数。为安全起见,请在设置完线程函数后立即调用这个函数。
这个函数是在2.0.4-alpha版本新引入的。
libevent可以检测使用事件时的一些常见错误并且进行报告。这些错误包括:
l将未初始化的event结构体当作已经初始化的。
l试图重新初始化一个挂起的event结构体。
跟踪哪些事件已经初始化需要使用额外的内存和处理器时间,所以只应该在真正调试程序的时候才启用调试模式。
必须在创建任何event_base之前调用这个函数。
如果在调试模式下使用大量由event_assign()(而不是event_new())创建的事件,程序可能会耗尽内存,这是因为没有方式可以告知libevent由event_assign()创建的事件不会再被使用了(可以调用event_free()告知由event_new()创建的事件已经无效了)。如果想在调试时避免耗尽内存,可以显式告知libevent由event_assign()创建的这些事件不再被当作已分配的了:
没有启用调试的时候调用event_debug_unassign没有效果。
这些调试函数在libevent2.0.4-alpha版本中加入。
新版本的libevent会添加特征,移除bug。有时候需要检测libevent的版本,以便:
l检测已安装的libevent版本是否可用于创建你的程序。
l为调试显示libevent的版本。
l检测libevent的版本,以便向用户警告bug,或者提示要做的工作。
宏返回编译时的libevent版本;函数返回运行时的libevent版本。注意:如果动态链接到libevent,这两个版本可能不同。
可以获取两种格式的libevent版本:用于显示给用户的字符串版本,或者用于数值比较的4字节整数版本。整数格式使用高字节表示主版本,低字节表示副版本,第三字节表示修正版本,最低字节表示发布状态:0表示发布,非零表示某特定发布版本的后续开发序列。
所以,libevent2.0.1-alpha发布版本的版本号是[02000100],或者说0x02000100。2.0.1-alpha和2.0.2-alpha之间的开发版本可能是[02
000108],或者说0x02000108。
本节描述的宏和函数定义在<event2/event.h>中。event_get_version函数首次出现在1.0c版本;其他的首次出现在2.0.1-alpha版本。
编写多线程程序的时候,在多个线程中同时访问同样的数据并不总是安全的。
libevent的结构体在多线程下通常有三种工作方式:
²某些结构体内在地是单线程的:同时在多个线程中使用它们总是不安全的。
²某些结构体具有可选的锁:可以告知libevent是否需要在多个线程中使用每个对象。
²某些结构体总是锁定的:如果libevent在支持锁的配置下运行,在多个线程中使用它们总是安全的。
为获取锁,在调用分配需要在多个线程间共享的结构体的libevent函数之前,必须告知libevent使用哪个锁函数。
如果使用pthreads库,或者使用Windows本地线程代码,那么你是幸运的:已经有设置好的libevent预定义函数能够正确的使用pthreads或者Windows函数。
接口
[cpp]view
plaincopy
#ifdefWIN32
intevthread_use_windows_threads(void);
#defineEVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED
#endif
#ifdef_EVENT_H***E_PTHREADS
intevthread_use_pthreads(void);
#defineEVTHREAD_USE_PTHREADS_IMPLEMENTED
#endif
这些函数在成功时都返回0,失败时返回-1。#ifdefWIN32
intevthread_use_windows_threads(void);
#defineEVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED
#endif
#ifdef_EVENT_H***E_PTHREADS
intevthread_use_pthreads(void);
#defineEVTHREAD_USE_PTHREADS_IMPLEMENTED
#endif
如果使用不同的线程库,则需要一些额外的工作,必须使用你的线程库来定义函数去实现:
l锁
l锁定
l解锁
l分配锁
l析构锁
l条件变量
l创建条件变量
l析构条件变量
l等待条件变量
l触发/广播某条件变量
l线程
l线程ID检测
使用evthread_set_lock_callbacks和evthread_set_id_callback接口告知libevent这些函数。
接口
[cpp]view
plaincopy
#defineEVTHREAD_WRITE0x04
#defineEVTHREAD_READ0x08
#defineEVTHREAD_TRY0x10
#defineEVTHREAD_LOCKTYPE_RECURSIVE1
#defineEVTHREAD_LOCKTYPE_READWRITE2
#defineEVTHREAD_LOCK_API_VERSION1
structevthread_lock_callbacks{
intlock_api_version;
unsignedsupported_locktypes;
void*(*alloc)(unsignedlocktype);
void(*free)(void*lock,unsignedlocktype);
int(*lock)(unsignedmode,void*lock);
int(*unlock)(unsignedmode,void*lock);
};
intevthread_set_lock_callbacks(conststructevthread_lock_callbacks*);
voidevthread_set_id_callback(unsignedlong(*id_fn)(void));
structevthread_condition_callbacks{
intcondition_api_version;
void*(*alloc_condition)(unsignedcondtype);
void(*free_condition)(void*cond);
int(*signal_condition)(void*cond,intbroadcast);
int(*wait_condition)(void*cond,void*lock,
conststructtimeval*timeout);
};
intevthread_set_condition_callbacks(
conststructevthread_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函数必须返回指定类型的新锁;#defineEVTHREAD_WRITE0x04
#defineEVTHREAD_READ0x08
#defineEVTHREAD_TRY0x10
#defineEVTHREAD_LOCKTYPE_RECURSIVE1
#defineEVTHREAD_LOCKTYPE_READWRITE2
#defineEVTHREAD_LOCK_API_VERSION1
structevthread_lock_callbacks{
intlock_api_version;
unsignedsupported_locktypes;
void*(*alloc)(unsignedlocktype);
void(*free)(void*lock,unsignedlocktype);
int(*lock)(unsignedmode,void*lock);
int(*unlock)(unsignedmode,void*lock);
};
intevthread_set_lock_callbacks(conststructevthread_lock_callbacks*);
voidevthread_set_id_callback(unsignedlong(*id_fn)(void));
structevthread_condition_callbacks{
intcondition_api_version;
void*(*alloc_condition)(unsignedcondtype);
void(*free_condition)(void*cond);
int(*signal_condition)(void*cond,intbroadcast);
int(*wait_condition)(void*cond,void*lock,
conststructtimeval*timeout);
};
intevthread_set_condition_callbacks(
conststructevthread_condition_callbacks*);
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(WindowsVista新引入的)函数文档。
示例
关于使用这些函数的示例,请查看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将给出断言失败并且退出。
接口
voidevthread_enable_lock_debuging(void);
注意
必须在创建或者使用任何锁之前调用这个函数。为安全起见,请在设置完线程函数后立即调用这个函数。这个函数是在2.0.4-alpha版本新引入的。
6调试事件的使用
libevent可以检测使用事件时的一些常见错误并且进行报告。这些错误包括:l将未初始化的event结构体当作已经初始化的。
l试图重新初始化一个挂起的event结构体。
跟踪哪些事件已经初始化需要使用额外的内存和处理器时间,所以只应该在真正调试程序的时候才启用调试模式。
接口
voidevent_enable_debug_mode(void);
必须在创建任何event_base之前调用这个函数。
如果在调试模式下使用大量由event_assign()(而不是event_new())创建的事件,程序可能会耗尽内存,这是因为没有方式可以告知libevent由event_assign()创建的事件不会再被使用了(可以调用event_free()告知由event_new()创建的事件已经无效了)。如果想在调试时避免耗尽内存,可以显式告知libevent由event_assign()创建的这些事件不再被当作已分配的了:
接口
voidevent_debug_unassign(structevent*ev);
没有启用调试的时候调用event_debug_unassign没有效果。
示例
[cpp]view
plaincopy
#include<event2/event.h>
#include<event2/event_struct.h>
#include<stdlib.h>
voidcb(evutil_socket_tfd,shortwhat,void*ptr)
{
/*Wepass'NULL'asthecallbackpointerfortheheapallocated
*event,andwepasstheeventitselfasthecallbackpointer
*forthestack-allocatedevent.*/
structevent*ev=ptr;
if(ev)
event_debug_unassign(ev);
}
/*Here'sasimplemainloopthatwaitsuntilfd1andfd2areboth
*readytoread.*/
voidmainloop(evutil_socket_tfd1,evutil_socket_tfd2,intdebug_mode)
{
structevent_base*base;
structeventevent_on_stack,*event_on_heap;
if(debug_mode)
event_enable_debug_mode();
base=event_base_new();
event_on_heap=event_new(base,fd1,EV_READ,cb,NULL);
event_assign(&event_on_stack,base,fd2,EV_READ,cb,&event_on_stack);
event_add(event_on_heap,NULL);
event_add(&event_on_stack,NULL);
event_base_dispatch(base);
event_free(event_on_heap);
event_base_free(base);
}
这些调试函数在libevent2.0.4-alpha版本中加入。#include<event2/event.h>
#include<event2/event_struct.h>
#include<stdlib.h>
voidcb(evutil_socket_tfd,shortwhat,void*ptr)
{
/*Wepass'NULL'asthecallbackpointerfortheheapallocated
*event,andwepasstheeventitselfasthecallbackpointer
*forthestack-allocatedevent.*/
structevent*ev=ptr;
if(ev)
event_debug_unassign(ev);
}
/*Here'sasimplemainloopthatwaitsuntilfd1andfd2areboth
*readytoread.*/
voidmainloop(evutil_socket_tfd1,evutil_socket_tfd2,intdebug_mode)
{
structevent_base*base;
structeventevent_on_stack,*event_on_heap;
if(debug_mode)
event_enable_debug_mode();
base=event_base_new();
event_on_heap=event_new(base,fd1,EV_READ,cb,NULL);
event_assign(&event_on_stack,base,fd2,EV_READ,cb,&event_on_stack);
event_add(event_on_heap,NULL);
event_add(&event_on_stack,NULL);
event_base_dispatch(base);
event_free(event_on_heap);
event_base_free(base);
}
7检测libevent的版本
新版本的libevent会添加特征,移除bug。有时候需要检测libevent的版本,以便:l检测已安装的libevent版本是否可用于创建你的程序。
l为调试显示libevent的版本。
l检测libevent的版本,以便向用户警告bug,或者提示要做的工作。
接口
[c-sharp]view
plaincopy
#defineLIBEVENT_VERSION_NUMBER0x02000300
#defineLIBEVENT_VERSION"2.0.3-alpha"
constchar*event_get_version(void);
ev_uint32_tevent_get_version_number(void);
宏返回编译时的libevent版本;函数返回运行时的libevent版本。注意:如果动态链接到libevent,这两个版本可能不同。#defineLIBEVENT_VERSION_NUMBER0x02000300
#defineLIBEVENT_VERSION"2.0.3-alpha"
constchar*event_get_version(void);
ev_uint32_tevent_get_version_number(void);
可以获取两种格式的libevent版本:用于显示给用户的字符串版本,或者用于数值比较的4字节整数版本。整数格式使用高字节表示主版本,低字节表示副版本,第三字节表示修正版本,最低字节表示发布状态:0表示发布,非零表示某特定发布版本的后续开发序列。
所以,libevent2.0.1-alpha发布版本的版本号是[02000100],或者说0x02000100。2.0.1-alpha和2.0.2-alpha之间的开发版本可能是[02
000108],或者说0x02000108。
示例:编译时检测
[cpp]view
plaincopy
#include<event2/event.h>
#if!defined(LIBEVENT_VERSION_NUMBER)||LIBEVENT_VERSION_NUMBER<0x02000100
#error"ThisversionofLibeventisnotsupported;Get2.0.1-alphaorlater."
#endif
int
make_sandwich(void)
{
/*Let'ssupposethatLibevent6.0.5introducesamake-me-a
sandwichfunction.*/
#ifLIBEVENT_VERSION_NUMBER>=0x06000500
evutil_make_me_a_sandwich();
return0;
#else
return-1;
#endif
}
#include<event2/event.h>
#if!defined(LIBEVENT_VERSION_NUMBER)||LIBEVENT_VERSION_NUMBER<0x02000100
#error"ThisversionofLibeventisnotsupported;Get2.0.1-alphaorlater."
#endif
int
make_sandwich(void)
{
/*Let'ssupposethatLibevent6.0.5introducesamake-me-a
sandwichfunction.*/
#ifLIBEVENT_VERSION_NUMBER>=0x06000500
evutil_make_me_a_sandwich();
return0;
#else
return-1;
#endif
}
示例:运行时检测
[cpp]view
plaincopy
#include<event2/event.h>
#include<string.h>
int
check_for_old_version(void)
{
constchar*v=event_get_version();
/*Thisisadumbwaytodoit,butitistheonlythingthatworks
beforeLibevent2.0.*/
if(!strncmp(v,"0.",2)||
!strncmp(v,"1.1",3)||
!strncmp(v,"1.2",3)||
!strncmp(v,"1.3",3)){
printf("YourversionofLibeventisveryold.Ifyourunintobugs,"
"considerupgrading./n");
return-1;
}else{
printf("RunningwithLibeventversion%s/n",v);
return0;
}
}
int
check_version_match(void)
{
ev_uint32_tv_compile,v_run;
v_compile=LIBEVENT_VERSION_NUMBER;
v_run=event_get_version_number();
if((v_compile&0xffff0000)!=(v_run&0xffff0000)){
printf("RunningwithaLibeventversion(%s)verydifferentfromthe"
"onewewerebuiltwith(%s)./n",event_get_version(),
LIBEVENT_VERSION);
return-1;
}
return0;
}
本节描述的宏和函数定义在<event2/event.h>中。event_get_version函数首次出现在1.0c版本;其他的首次出现在2.0.1-alpha版本。
#include<event2/event.h>
#include<string.h>
int
check_for_old_version(void)
{
constchar*v=event_get_version();
/*Thisisadumbwaytodoit,butitistheonlythingthatworks
beforeLibevent2.0.*/
if(!strncmp(v,"0.",2)||
!strncmp(v,"1.1",3)||
!strncmp(v,"1.2",3)||
!strncmp(v,"1.3",3)){
printf("YourversionofLibeventisveryold.Ifyourunintobugs,"
"considerupgrading./n");
return-1;
}else{
printf("RunningwithLibeventversion%s/n",v);
return0;
}
}
int
check_version_match(void)
{
ev_uint32_tv_compile,v_run;
v_compile=LIBEVENT_VERSION_NUMBER;
v_run=event_get_version_number();
if((v_compile&0xffff0000)!=(v_run&0xffff0000)){
printf("RunningwithaLibeventversion(%s)verydifferentfromthe"
"onewewerebuiltwith(%s)./n",event_get_version(),
LIBEVENT_VERSION);
return-1;
}
return0;
}
相关文章推荐
- Libevent参考手册第一章:设置libevent
- Libevent参考手册第一章:设置libevent(一)
- [翻译]Libevent 参考手册(一) -- 设置 Libevent 库
- Libevent参考手册第一章:设置libevent(二)
- Libevent参考手册:设置libevent(一)
- Libevent参考手册第一章:设置libevent (三)
- Libevent参考手册第一章:设置libevent(一)
- Libevent参考手册第一章:设置libevent(二)
- smb设置参考手册
- ARM920T技术参考手册&amp;amp;S3C2440汇编指令设置
- Libevent参考手册:前言
- Libevent参考手册:连接监听器:接受TCP连接 (2011-08-02 20:56:19)
- libevent参考手册第六章:bufferevent:概念和入门
- Libevent参考手册第七章:Evbuffers用于缓冲IO的实用工具
- 关于Solaris的参考手册的查看设置
- [翻译]libevent参考手册第六章:bufferevent:概念和入门 (2011-04-30 19:26:17)
- Libevent参考手册:Bufferevent:高级话题 (2011-07-17 22:22:20)
- 翻译:Libevent参考手册:前言
- smb设置参考手册
- Libevent参考手册第八章:连接侦听器: 接受 TCP 连接