libevent源码学习(3):线程锁、条件变量(一)(锁函数、条件变量函数设置)

    xiaoxiao2022-07-03  119

    目录

    锁与条件变量函数结构体

    锁函数结构体

    设置自定义的锁函数

    条件变量函数结构体

    设置自定义的条件变量函数

    使用posix线程函数

    默认的锁函数

    evthread_posix_lock_alloc

    evthread_posix_lock_free

    evthread_posix_lock

    evthread_posix_unlock

    默认的条件变量函数

    evthread_posix_cond_alloc

    evthread_posix_cond_free

    evthread_posix_cond_signal

    evthread_posix_cond_wait

    再探锁函数设置接口

    再探条件变量函数设置接口

    线程ID获取函数接口

    总结


    以下源码均基于libevent-2.0.21-stable。

            libevent中,使用了很多函数,而这么多的函数并不是所有都是对用户开放的,这一些函数声明都放在include文件夹下以供用户调用,而另一大部分不在其中的函数,则为libevent的内部调用函数。接下来要分析libevent的线程锁与条件变量,那么就先来看用户所能使用的接口有哪些,在include/event2文件夹下,可以看到一个thread.h的文件,因此就先从该文件入手开始分析。

    锁与条件变量函数结构体

    锁函数结构体

               锁对应的结构体为evthread_lock_callbacks,其定义如下:

    (thread.h)

    struct evthread_lock_callbacks {    /** The current version of the locking API.  Set this to     * EVTHREAD_LOCK_API_VERSION */    int lock_api_version;    /** Which kinds of locks does this version of the locking API     * support?  A bitfield of EVTHREAD_LOCKTYPE_RECURSIVE and     * EVTHREAD_LOCKTYPE_READWRITE.     *     * (Note that RECURSIVE locks are currently mandatory, and     * READWRITE locks are not currently used.)     **/    unsigned supported_locktypes;    /** Function to allocate and initialize new lock of type 'locktype'.     * Returns NULL on failure. */   //以下都是函数指针    void *(*alloc)(unsigned locktype);    //分配一个指定类型的锁,如果分配失败返回NULL    /** Funtion to release all storage held in 'lock', which was created     * with type 'locktype'. */    void (*free)(void *lock, unsigned locktype);   //释放一个指定类型的锁,分配成功返回0,否则失败    /** Acquire an already-allocated lock at 'lock' with mode 'mode'.     * Returns 0 on success, and nonzero on failure. */    int (*lock)(unsigned mode, void *lock);     //对一个已分配的锁进行加锁,锁的模式为read/write/try,获取成功返回0    /** Release a lock at 'lock' using mode 'mode'.  Returns 0 on success,     * and nonzero on failure. */    int (*unlock)(unsigned mode, void *lock);   //对一个已持有的锁进行解锁,解锁成功返回0;需指定锁的模式 };

            锁结构体一共有6个变量,后面4个成员为函数指针,分别指向锁的分配(alloc)、释放(free)、上锁(lock)和解锁(unlock)四个函数,由此可见,libevent中的锁函数也是可以用户自己定制的。可以看到,这些锁函数中会用到的参数有锁变量lock、锁类型locktype以及锁模式mode,锁变量自然不必说,那么锁类型和锁模式是什么呢?

            对于当前版本的libevent,thread.h中宏定义了WRITE、READ和TRY三种模式,以及RECURSIVE和READWRITE两种类型,如下所示。

    #define EVTHREAD_WRITE    0x04      //用于读写锁中的写模式

    #define EVTHREAD_READ    0x08       //用于读写锁中的读模式

    #define EVTHREAD_TRY    0x10      //尝试加锁模式,如果获取不到锁也不用阻塞 #define EVTHREAD_LOCKTYPE_RECURSIVE 1        //递归锁,同一线程可以多次持有同一个锁,其他线程如果想获取该锁,需要持有线程进行加锁次数的解锁,此时递归锁才能被其他线程持有 #define EVTHREAD_LOCKTYPE_READWRITE 2      //读写锁,可以多个线程同时以读模式持有同一个锁,但是只能有一个线程以写模式持有该锁,如果该锁被写模式持有,那么其他线程也无法以读模式持有该锁

            实际上还有默认的普通锁类型和模式,值为0,这里没有直接写出来,但是在后面的函数中会有所体现。

            再回到前两个成员变量中。supported_locktypes就不用说了,就是指的锁的类型。lock_api_version从字面意义上将是锁的API版本,根据上面相关的英文说明,lock_api_version的位域以及目前锁类型(当前为递归锁和读写锁)的定义反映了当前版本的libevent所支持的锁类型,并且其中还提到,目前版本只使用了递归锁,没有使用读写锁,而当前的版本由宏定义EVTHREAD_LOCK_API_VERSION确定,其定义为

    #define EVTHREAD_LOCK_API_VERSION 1

            如何理解呢?根据我的理解,递归锁宏定义为1,读写锁为2,lock_api_version应当是其所支持的锁进行位或的结果就是,举个例子,如果只支持递归锁,那么lock_api_version就是1,与这里相对应;如果只支持读写锁,那么lock_api_version就应该是2;如果二者都支持,那么lock_api_version就应该为3了。这样,通过lock_api_version的位域也就能反映出当前版本支持哪些lock了。因此,如果后面拓展到其他类型的锁,那么锁宏定义应当为4、8....

    设置自定义的锁函数

            如同libevent的日志、错误处理以及内存管理函数一样,锁函数也可以进行定制,定制接口为evthread_set_lock_callbacks函数。其原型为:

    int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *);

            原型很简单,传入一个锁函数结构体即可,调用该函数后,libevent中一旦需要使用锁的相关函数,那么就会自动调用传入的结构体中相对应的函数。该函数的实现在后面进行分析。

    条件变量函数结构体

            条件变量对应结构体为evthread_condition_callbacks,其定义如下:

    struct evthread_condition_callbacks {    /** The current version of the conditions API.  Set this to     * EVTHREAD_CONDITION_API_VERSION */    int condition_api_version;    /** Function to allocate and initialize a new condition variable.     * Returns the condition variable on success, and NULL on failure.     * The 'condtype' argument will be 0 with this API version.     */    void *(*alloc_condition)(unsigned condtype);     //分配并初始化一个条件变量,分配成功就返回这个条件变量,否则返回NULL,当前版本下的condtype为0    /** Function to free a condition variable. */    void (*free_condition)(void *cond);   //释放一个条件变量    /** Function to signal a condition variable.  If 'broadcast' is 1, all     * threads waiting on 'cond' should be woken; otherwise, only on one     * thread is worken.  Should return 0 on success, -1 on failure.     * This function will only be called while holding the associated     * lock for the condition.     */    int (*signal_condition)(void *cond, int broadcast);   //用于通知条件变量    // 如果设置了broadcast,则为广播,所有该条件变量都会被唤醒(可能有多个线程持有该条件变量)    // 否则,就只有一个条件变量被唤醒。成功返回0,失败返回-1,只有条件变量所在线程持有锁的情况下才会被调用    /** Function to wait for a condition variable.  The lock 'lock'     * will be held when this function is called; should be released     * while waiting for the condition to be come signalled, and     * should be held again when this function returns.     * If timeout is provided, it is interval of seconds to wait for     * the event to become signalled; if it is NULL, the function     * should wait indefinitely.     *     * The function should return -1 on error; 0 if the condition     * was signalled, or 1 on a timeout. */    int (*wait_condition)(void *cond, void *lock,        const struct timeval *timeout);   //进入休眠状态等待条件变量被唤醒 };

            条件变量结构体中有一个condition_api_version,这个变量目前没有什么实质的作用,留在这里应该是作为以后一个可能的扩展,因为目前linux pthread中的条件变量也只定义了cond_attr,但是没有具体的实现。

            后四个成员分别对应条件变量的分配(alloc)、释放(free)、通知(signal)以及等待(wait)四个函数。其中alloc函数是分配一个条件变量,不过需要传入一个condtype,如上所述,这个参数是用来方便以后的扩展,在当前版本下传入condtype = 0即可。free函数是释放一个条件变量,这里不用多说,值得注意的是剩下两个:条件变量唤醒函数以及等待函数。

            条件变量唤醒函数signal_condition是用来唤醒一个条件变量等待线程,和pthread_cond_signal相比,都需要以条件变量cond作为参数,不过它还多了一个参数broadcast,根据函数注释也可以知道,该函数是将pthread_cond_signal与pthread_cond_broadca集成起来了,由参数broadcast来决定是唤醒单一等待线程还是所有等待线程。

             然后是条件变量等待函数,需要传入条件变量cond,锁变量lock以及一个超时结构体,可知,该函数也是集成了pthread_cond_wait和pthread_cond_timewait的功能,通过参数timeout来设置超时时间。不过另一个问题需要注意的是,这里也是需要传入一个lock的,因为在调用wait来等待条件变量时,会先将保护临界区的锁lock给释放掉,等到被唤醒时再重新持有lock,这样就避免了唤醒线程因为需要持有同一把锁而阻塞引起死锁的问题。

    设置自定义的条件变量函数

            定制条件变量函数的接口为evthread_set_condition_callbacks,与lock的定制函数类似,这里就不多说了。


           根据前面所分析的,libevent中可以自定义锁和条件变量函数,而实际上,libevent中已经设定好了posix以及windows的相关函数,如果在类unix系统下可以直接使用evthread_use_pthreads函数来使用posix的锁函数与条件变量函数,在windows下可以直接使用evthread_use_windows_threads函数来使用windows的锁与条件变量函数,这样就不必我们自己进行定制了。以下就以linux系统为例,windows与之类似就不多做分析了。

    使用posix线程函数

            evthread_use_pthreads函数定义如下所示:

    int evthread_use_pthreads(void) { struct evthread_lock_callbacks cbs = { EVTHREAD_LOCK_API_VERSION, EVTHREAD_LOCKTYPE_RECURSIVE, evthread_posix_lock_alloc, evthread_posix_lock_free, evthread_posix_lock, evthread_posix_unlock }; struct evthread_condition_callbacks cond_cbs = { EVTHREAD_CONDITION_API_VERSION, evthread_posix_cond_alloc, evthread_posix_cond_free, evthread_posix_cond_signal, evthread_posix_cond_wait }; /* Set ourselves up to get recursive locks. */ if (pthread_mutexattr_init(&attr_recursive)) //初始化 return -1; if (pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE)) //将互斥锁的属性设置为递归锁 return -1; evthread_set_lock_callbacks(&cbs); //设置_evthread_lock_fns全局变量 evthread_set_condition_callbacks(&cond_cbs); //设置_evthread_cond_fns全局变量 evthread_set_id_callback(evthread_posix_get_id); //指向获取线程id的函数 设置_evthread_id_fn全局变量 return 0; }

              在evthread_use_pthreads函数中,分别定义了锁函数结构体cbs和条件变量函数结构体cond_cbs,在cbs中设定了posix下默认的锁函数,并且锁的类型均设置为了递归锁,而在cond_cbs中,则设定了posix下的条件变量函数,现在先来分析一下默认的锁函数以及条件变量函数。

    默认的锁函数

    evthread_posix_lock_alloc

             evthread_posix_lock_alloc函数定义如下:

    static pthread_mutexattr_t attr_recursive; static void * evthread_posix_lock_alloc(unsigned locktype) // { pthread_mutexattr_t *attr = NULL; //互斥锁属性 pthread_mutex_t *lock = mm_malloc(sizeof(pthread_mutex_t)); //互斥锁 if (!lock) //如果互斥锁获取失败 return NULL; if (locktype & EVTHREAD_LOCKTYPE_RECURSIVE) //递归锁为1, 因此不管locktype为多少,locktype & EVTHREAD_LOCKTYPE_RECURSIVE只能为0或1, // 由此可见,如果使用默认的posix锁函数,是只支持递归锁或者普通锁的 attr = &attr_recursive; if (pthread_mutex_init(lock, attr)) { //如果locktype & EVTHREAD_LOCKTYPE_RECURSIVE为假,那么就用默认的锁属性PTHREAD_MUTEX_TIMED_NP即普通锁,否则为递归锁 mm_free(lock); return NULL; } return lock; }

            该函数的作用是分配一个递归锁或者普通锁。这里需要注意到locktype & EVTHREAD_LOCKTYPE_RECURSIVE这一判断条件。EVTHREAD_LOCKTYPE_RECURSIVE为递归锁类型,其宏定义为1,因此不管locktype为多少,二者位与后的结果只能为0或者为1,如果为1,就会执行attr = &attr_recursive;注意到attr_recursive这个变量,它是个静态变量,在调用evthread_use_pthreads时,pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE)会将其设置为递归锁属性(PTHREAD_MUTEX_RECURSIVE是posix内置的宏定义)。

            因此,如果locktype为1,那么attr就是递归锁属性,最终lock就是一个递归锁;如果locktype为其他值(考虑现在锁的类型只有0,1,2),那么最终分配的就是一个普通锁。

    evthread_posix_lock_free

           evthread_posix_lock_free函数定义如下:

    static void evthread_posix_lock_free(void *_lock, unsigned locktype) //这里locktype没有使用但是也需要用到两个参数的原因在于:释放锁的函数不一定会调用posix的函数 //如果开启了调试锁,那么释放锁的函数是需要用到Locktype的,因此为了兼容,这里也需要locktype作为参数 { pthread_mutex_t *lock = _lock; pthread_mutex_destroy(lock); //销毁锁 mm_free(lock); }

            该函数只是进行了一个简单的释放锁操作,需要注意的是,这里函数传入了两个参数,但是第二个参数并没有用上,其存在的原因是因为后面调试锁也会有一个free函数,而在调试锁的free函数中是需要用到该参数的,而调试锁的free和posix_lock_free又必须统一接口,因此这里就需要一个locktype参数。

    evthread_posix_lock

           evthread_posix_lock函数定义如下:

    static int evthread_posix_lock(unsigned mode, void *_lock) //默认的posix的lock也并不支持读写锁 { pthread_mutex_t *lock = _lock; if (mode & EVTHREAD_TRY) //当且仅当mode为EVTHREAD_TRY时,进行try_lock,否则就是lock return pthread_mutex_trylock(lock); else return pthread_mutex_lock(lock); }

              根据该函数可以知道,在默认的Posix函数下,只识别EVTHREAD_TRY的锁模式。由于EVTHREAD_TRY的宏定义为0x10,而READ和WRITE为0x04和0x08,因此这里的mode & EVTHREAD_TRY也是用来区分出锁的模式是TRY还是非TRY。

    evthread_posix_unlock

             evthread_posix_unlock函数定义如下:

    static int evthread_posix_unlock(unsigned mode, void *_lock) { pthread_mutex_t *lock = _lock; return pthread_mutex_unlock(lock); }

             该函数用于解锁,这里多出来的mode参数与前面free函数的作用一样,这里就不多说了。

    默认的条件变量函数

    evthread_posix_cond_alloc

             evthread_posix_cond_alloc函数定义如下:

    static void * evthread_posix_cond_alloc(unsigned condflags) { pthread_cond_t *cond = mm_malloc(sizeof(pthread_cond_t)); if (!cond) return NULL; if (pthread_cond_init(cond, NULL)) { //分配并初始化一个条件变量 mm_free(cond); return NULL; } return cond; }

    evthread_posix_cond_free

             evthread_posix_cond_free函数定义如下:

    static void evthread_posix_cond_free(void *_cond) //释放条件变量 { pthread_cond_t *cond = _cond; pthread_cond_destroy(cond); mm_free(cond); }

    evthread_posix_cond_signal

             evthread_posix_cond_signal函数定义如下:

    static int evthread_posix_cond_signal(void *_cond, int broadcast) { pthread_cond_t *cond = _cond; int r; if (broadcast) //广播唤醒条件变量 r = pthread_cond_broadcast(cond); else //唤醒一个条件变量 r = pthread_cond_signal(cond); return r ? -1 : 0; }

    evthread_posix_cond_wait

            evthread_posix_cond_wait函数定义如下:

    static int evthread_posix_cond_wait(void *_cond, void *_lock, const struct timeval *tv) { int r; pthread_cond_t *cond = _cond; pthread_mutex_t *lock = _lock; if (tv) { struct timeval now, abstime; struct timespec ts; evutil_gettimeofday(&now, NULL); //获取现在的时间(从1970.1.1到现在) evutil_timeradd(&now, tv, &abstime); ts.tv_sec = abstime.tv_sec; ts.tv_nsec = abstime.tv_usec*1000; r = pthread_cond_timedwait(cond, lock, &ts); if (r == ETIMEDOUT) //超时 return 1; else if (r) //出错 return -1; else //成功 return 0; } else { //如果没有设置等待时间 就是无限等待 r = pthread_cond_wait(cond, lock); return r ? -1 : 0; } }

    该函数用来等待条件变量唤醒,如果tv为NULL,那么就会调用pthread_cond_wait函数,否则就会调用pthread_cond_timedwait进行超时等待,如果超时未唤醒则返回1,出错返回负1,否则返回0。需要注意的是,pthread_cond_timewait函数的参数ts为绝对时间(相对于格林尼治时间),而ts则是由abstime得到。再来看abstime,它是由当前时间now加上传入时间tv得到的,因此,evthread_posix_cond_wait传入的超时结构体应为相对时间而非绝对时间。

          在evthread_use_pthreads函数的最后,还分别调用了evthread_set_lock_callbacks、evthread_set_condition_callbacks和evthread_set_id_callback函数来设置锁函数、条件变量函数以及线程ID获取函数,现在对其进行分析。

    再探锁函数设置接口

          锁函数的设置接口为evthread_set_lock_callbacks,该函数定义如下:

    int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *cbs) { struct evthread_lock_callbacks *target = _evthread_lock_debugging_enabled ? &_original_lock_fns : &_evthread_lock_fns; //如果开启了调试锁,则target指向开启调试锁之前的锁函数结构体,否则target指向调试锁的结构体 if (!cbs) { //如果传入的结构体为空 if (target->alloc) //如果alloc非空,说明线程锁函数结构体已经设置过了,就报警,提醒用户接下来会将设置过的锁函数全部清空,线程锁函数可能无法正常使用 event_warnx("Trying to disable lock functions after " "they have been set up will probaby not work."); memset(target, 0, sizeof(_evthread_lock_fns)); //由于target是指针,因此相当于将锁函数结构体也全部清空了。 return 0; } //传入的锁函数结构体非空 if (target->alloc) {//如果此时alloc非空,说明已经设置过锁函数结构体了 /* Uh oh; we already had locking callbacks set up.*///如果已经设置过锁函数结构体,再次进行设置时只允许传入的锁函数结构体与已设置的锁函数结构体完全相同的情况 if (target->lock_api_version == cbs->lock_api_version && target->supported_locktypes == cbs->supported_locktypes && target->alloc == cbs->alloc && target->free == cbs->free && target->lock == cbs->lock && target->unlock == cbs->unlock) { /* no change -- allow this. */ return 0; } event_warnx("Can't change lock callbacks once they have been " "initialized."); //提醒用户,锁函数结构体一旦被设置,就不能再更改了。 return -1; } //执行到这说明cbs非空,并且还没有设置过锁函数 if (cbs->alloc && cbs->free && cbs->lock && cbs->unlock) { //检查4个函数是否有未定义的,如果有一个函数未定义,那么就直接返回, // 这也就说明了为什么前面可以只通过alloc就可以判断锁函数结构体是否已经被设置过了,4个函数是必须同时非空才能被设置的 memcpy(target, cbs, sizeof(_evthread_lock_fns)); //将传入的锁结构复制到target中 return event_global_setup_locks_(1); //执行到这一句的_evthread_lock_fns或者_original_lock_fns是不可能为空的 } else { return -1; } }

             在该函数的开头,使用了一个三目运算符,其中涉及到了_evthread_lock_debugging_enabled、_original_lock_fns以及_evthread_lock_fns,这一句话与后面会分析到的调试锁有关系,这里可以先暂时不用管,只需要知道函数开始执行时target是指向现在已设置的锁函数结构体,函数执行到最后target就是最终指向设置完成的锁函数。

             然后接着向下看,会判断cbs是否为空,如果为空,就相当于不设置锁函数。如果此时target->alloc不为空,说明此时已经设置过锁函数,如果继续设置其为空的话,那么现在的线程锁函数就不能再使用了,就向用户提出警告。接着用memset对锁函数结构体进行清空。

            如果cbs非空,那么就又要分两种情况了:target是否为空?

           如果target非空,说明已经设置过了锁函数,此时的操作相当于修改锁函数,实际上,这种情况是不被允许的,因为你之前已经设置过了锁函数,如果你在线程中已经使用过了这些函数,此时又来修改这些锁函数,很有可能修改之后还仍然有些线程正在使用之前的锁函数,比如正在调用之前的锁函数加锁阻塞中,那么就很容易出现问题的。不过在源码中可以看到,在target非空(target->alloc非空)的情况下,如果传入的cbs与现有的target是完全相同的,那么就相当于没有任何改变,这是唯一允许的,返回0,除此之外的所有情况,则直接报错并返回-1。

          如果target为空,说明还没有设置过锁函数,那么直接根据cbs来设置target即可,这里需要注意的是,设置锁函数时,必须四个锁函数都非空(缺少其中一个从逻辑上都是没有意义的),这样才能设置,也就是说,四个锁函数要么同时为空,要么同时非空,这也是为什么前面可以通过target->alloc是否为空就可以判断是否设置过锁函数了。最后使用memcpy来将cbs赋值到target中即可,由于target指向最终使用的锁函数结构体,因此到这里就相当于锁函数设置完毕。

           注意:后面虽然还调用了event_global_setup_locks_函数,但是到这里已经完成了锁函数的设置,因此该函数的调用对锁函数的设置是没有关系的。而且通过event_global_setup_locks_函数的定义也可以知道,该函数是用于设置_event_debug_map_lock、evsig_base_lock和arc4rand_lock三个全局变量的,而这三个全局变量目前在整个libevent中没有怎么使用,因此可以暂时不用管它。

    再探条件变量函数设置接口

           条件变量函数的设置接口为evthread_set_condition_callbacks,该函数定义如下:

    int evthread_set_condition_callbacks(const struct evthread_condition_callbacks *cbs) { struct evthread_condition_callbacks *target = _evthread_lock_debugging_enabled ? &_original_cond_fns : &_evthread_cond_fns; if (!cbs) { //如果传入的参数为空,相当于要清空当前的条件变量函数 if (target->alloc_condition) event_warnx("Trying to disable condition functions " "after they have been set up will probaby not " "work."); memset(target, 0, sizeof(_evthread_cond_fns)); return 0; } if (target->alloc_condition) { //如果已经设置过条件变量函数了 就只允许没有任何修改的修改 /* Uh oh; we already had condition callbacks set up.*/ if (target->condition_api_version == cbs->condition_api_version && target->alloc_condition == cbs->alloc_condition && target->free_condition == cbs->free_condition && target->signal_condition == cbs->signal_condition && target->wait_condition == cbs->wait_condition) { /* no change -- allow this. */ return 0; } event_warnx("Can't change condition callbacks once they " "have been initialized."); return -1; } //执行到这里说明cbs非空,并且还没有设置过条件变量函数 if (cbs->alloc_condition && cbs->free_condition && cbs->signal_condition && cbs->wait_condition) { memcpy(target, cbs, sizeof(_evthread_cond_fns)); //如果四个条件变量函数都非空,就直接设置即可 } if (_evthread_lock_debugging_enabled) { //如果开启了调试锁, _evthread_cond_fns.alloc_condition = cbs->alloc_condition; _evthread_cond_fns.free_condition = cbs->free_condition; _evthread_cond_fns.signal_condition = cbs->signal_condition; } return 0; }

            实际上条件变量函数设置接口与前面的锁函数设置接口基本上是一样的,相类似的地方就不说了,唯一不同的地方是条件变量设置函数的最后还多了一个if (_evthread_lock_debugging_enabled) 的语句,这一点会在后面调试锁的地方进行分析。

    线程ID获取函数接口

            线程ID获取函数接口为evthread_set_id_callback,该函数定义如下:

    void evthread_set_id_callback(unsigned long (*id_fn)(void)) { _evthread_id_fn = id_fn; }

             该函数看上去非常简单,就是让_evthread_id_fn这个全局变量指向了传入的函数指针参数,对于它的作用,可以参考evthread_use_pthreads函数中对其的调用evthread_set_id_callback(evthread_posix_get_id),调用的参数为evthread_posix_get_id,这也是一个参数,其定义为:

    static unsigned long evthread_posix_get_id(void) { union { //联合体,内部成员的地址相同 pthread_t thr; #if _EVENT_SIZEOF_PTHREAD_T > _EVENT_SIZEOF_LONG ev_uint64_t id; #else unsigned long id; #endif } r; #if _EVENT_SIZEOF_PTHREAD_T < _EVENT_SIZEOF_LONG memset(&r, 0, sizeof(r)); #endif r.thr = pthread_self(); return (unsigned long)r.id; }

             r是一个联合体,有两个成员thr和id,联合体的特点是联合体内部的成员地址是相同的,然后看到r.thr = pthread_self(),这是获取当前线程的id然后放到thr中,由于r.id与r.thr地址相同,而pthread_t本身就是unsigned long类型,因此实际上返回的r.id就等于获取到的线程id。

            由此也可以知道:evthread_set_id_callback函数就是用来指定获取当前线程id的函数,这一点在后面调试锁中会有更多体会。

    总结

            如同前面的日志及错误处理与内存管理,libevent的线程锁与条件变量函数也是可以用户自行设置的,也可以直接调用evthread_use_pthreads或evthread_use_windows_threads函数来使用已设置好的posix线程函数或windows线程函数。不过就以上分析而言,还有问题没有解决:

    evthread_set_lock_callbacks中的_original_lock_fns 和_evthread_lock_fns是什么?

    evthread_set_condition_callbacks中的_original_lock_fns 和_evthread_cond_fns是什么?

    上面两个函数中都出现的_evthread_lock_debugging_enabled又是什么?

    为什么evthread_set_condition_callbacks的最后,设置了alloc、free和signal,却唯独没有设置wait?

            以上问题,将在下一篇分析调试锁的文章中得到解决。

    最新回复(0)