libevent的锁机制之evthread_use_pthreads()介绍

libevent提供了一套内置的锁机制,通过调用`evthread_use_pthreads()`,开发者可以启用多线程支持并自动应用锁机制。该函数初始化了互斥锁和条件变量,包括锁的分配、释放和操作。evthread_lock_callbacks和evthread_condition_callbacks结构体定义了锁和条件变量的回调函数,如锁的分配、释放、上锁和解锁。文章详细解释了这些回调函数的实现,以及如何设置和使用这些回调。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于linux libevent已经提供了一套锁机制,我们就不用自己在写一些锁相关的回掉函数(用evthread_set_lock_callbacks设置)供libevent使用,
我们在linux上使用锁机制的时候只要使用int evthread_use_pthreads(void) 开启使用多线程机制,那么锁的机制也会自动加入进来

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;

    //将cbs结构内容保存在全局变量中evthread_lock_fns_   用于后续锁的操作
    //当然如果使用evthread_enable_lock_debuging()开启锁的debug模式,则此处的全局变量就是original_lock_fns_
    evthread_set_lock_callbacks(&cbs);

    //将cond_cbs结构内容保存到全局变量 evthread_cond_fns_用于后续条件变量的使用
    //当然如果使用evthread_enable_lock_debuging()开启锁的debug模式,则此处的全局变量就是original_cond_fns_
    evthread_set_condition_callbacks(&cond_cbs);

    //将获取线程自身id的方式保存到evthread_id_fn_全局变量 用于后续线程获得自己的id
    //对于linux来说此处是pthread_self() 
    evthread_set_id_callback(evthread_posix_get_id);
    return 0;
}

那么接下来我们看下evthread_lock_callbacks结构体如下:
struct evthread_lock_callbacks {
    int lock_api_version;//锁api的版本号  一般设置为 EVTHREAD_LOCK_API_VERSION
    unsigned supported_locktypes;//锁的类型 EVTHREAD_LOCKTYPE_RECURSIVE递归锁  EVTHREAD_LOCKTYPE_READWRITE读写锁
    void *(*alloc)(unsigned locktype);//分配一个锁的对象
    void (*free)(void *lock, unsigned locktype);//释放一个锁的对象
    int (*lock)(unsigned mode, void *lock);   //对锁进行上锁
    int (*unlock)(unsigned mode, void *lock); //解锁
};

在evthread_use_pthreads中我们初始化此结构如下
struct evthread_lock_callbacks cbs = 
{
    EVTHREAD_LOCK_API_VERSION,  //首先版本设置为1 
    EVTHREAD_LOCKTYPE_RECURSIVE, //锁的类型为递归锁 允许一个线程多次访问
    evthread_posix_lock_alloc,   //锁对象的分配方式  看后面
    evthread_posix_lock_free,    //锁对象的释放方式  看后面
    evthread_posix_lock,         //上锁方式  看后面
    evthread_posix_unlock        //解锁方式  看后面
};

//对锁的分配初始化
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;

    //锁的类型 还记得evthread_use_pthreads中初始化的锁类型
    if (locktype & EVTHREAD_LOCKTYPE_RECURSIVE)
        attr = &attr_recursive;

    //设置锁的属性 并进行初始化
    if (pthread_mutex_init(lock, attr)) {
        mm_free(lock);
        return NULL;
    }
    //返回锁的对象
    return lock;
}

//对锁对象的销毁释放
static void evthread_posix_lock_free(void *lock_, unsigned locktype)
{
    pthread_mutex_t *lock = lock_;
    //销毁一个锁
    pthread_mutex_destroy(lock);
    mm_free(lock);
}

//对锁进行上锁
static int evthread_posix_lock(unsigned mode, void *lock_)
{
    pthread_mutex_t *lock = lock_;
    if (mode & EVTHREAD_TRY)
        return pthread_mutex_trylock(lock);
    else
        return pthread_mutex_lock(lock);
}

//对锁进行解锁
static int evthread_posix_unlock(unsigned mode, void *lock_)
{
    pthread_mutex_t *lock = lock_;
    return pthread_mutex_unlock(lock);
}

接下来我们看看对条件变量的使用先看结构struct evthread_condition_callbacks如下:
struct evthread_condition_callbacks {
    int condition_api_version; //条件变量API版本 设置为EVTHREAD_CONDITION_API_VERSION
    void *(*alloc_condition)(unsigned condtype);//分配一个条件变量对象
    void (*free_condition)(void *cond);         //释放一个条件变量对象
    int (*signal_condition)(void *cond, int broadcast);//发送信号等待此条件变量的线程
    int (*wait_condition)(void *cond, void *lock,const struct timeval *timeout);//等待此条件变量的信号阻塞当前线程
};

然后看下在evthread_use_pthreads对此结构的初始化
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
};

//分配一个条件变量对象
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;
}

//释放一个条件变量对象
static void evthread_posix_cond_free(void *cond_)
{
    pthread_cond_t *cond = cond_;
    pthread_cond_destroy(cond);
    mm_free(cond);
}

//发送信号给 因等待此环境变量而组赛的线程
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;
}

//等待此环境变量 阻塞线程
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);
        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;
    }
}

我们在evthread_use_pthreads()中还有几个函数要介绍如下:
evthread_set_lock_callbacks(&cbs);
evthread_set_condition_callbacks(&cond_cbs);
evthread_set_id_callback(evthread_posix_get_id);

//设置锁的回掉
int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *cbs)
{
    //根据是否开启lock的debug模式返回不通的全局变量地址
    //&original_lock_fns_ 或则和 &evthread_lock_fns_
    //在程序开始若使用evthread_enable_lock_debuging()则会返回&original_lock_fns_ 否则返回&evthread_lock_fns_
    //其中original_lock_fns_中存放用户定制的锁相关操作,而evthread_lock_fns_存放当前要操作的锁相关操作,
    //如果先于evthread_use_pthreads()开启锁的调试功能evthread_lock_fns_中将存放debug_lock_xx 但最终还是会调用到original_lock_fns_中的锁操作
    struct evthread_lock_callbacks *target = evthread_get_lock_callbacks();

    //在编译libevent的时候没有使用--disable-debug-mode
#ifndef EVENT__DISABLE_DEBUG_MODE
    if (event_debug_mode_on_) //是否开启了debug模式,用 event_enable_debug_mode() 开启debug模式
    {
        //在创建even_base的时候会将此值设置为1 ,  如果为1 说明没有在使用event_base之前调用此函数 可能会引起错误
        if (event_debug_created_threadable_ctx_) {
            event_errx(1, "evthread initialization must be called BEFORE anything else!");
        }
    }
#endif

    //传入的参数为空的话
    if (!cbs) 
    {
        //已经设置了相关锁的操作
        if (target->alloc)
            event_warnx("Trying to disable lock functions after ""they have been set up will probaby not work.");
        //传入null值的话  会将锁的相关操作清空
        memset(target, 0, sizeof(evthread_lock_fns_));
        return 0;
    }

    //如果全局变量中已经存在了锁的相关操作
    if (target->alloc) 
    {
        /* Uh oh; we already had locking callbacks set up.*/
       //设置的锁的操作是一个 直接返回0
        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;
    }

    //将锁的相关操作保存到全局变量中
    if (cbs->alloc && cbs->free && cbs->lock && cbs->unlock) {
        memcpy(target, cbs, sizeof(evthread_lock_fns_));
        return event_global_setup_locks_(1); //主要分配一些全局锁 看后面介绍
    } 
    else 
    {
        return -1;
    }
}

//设置环境变量相关操作的回掉
int evthread_set_condition_callbacks(const struct evthread_condition_callbacks *cbs)
{
    //根据是否开启lock的debug模式返回不通的全局变量地址
    //&original_cond_fns_ 或者 &evthread_cond_fns_;
    //在程序开始若使用evthread_enable_lock_debuging()则会返回&original_cond_fns_ 否则返回&evthread_cond_fns_
    struct evthread_condition_callbacks *target = evthread_get_condition_callbacks();

    //在编译libevent的时候没有使用--disable-debug-mode
#ifndef EVENT__DISABLE_DEBUG_MODE
    if (event_debug_mode_on_) {//是否开启了debug模式,用 event_enable_debug_mode() 开启debug模式

        //在创建even_base的时候会将此值设置为1 ,  如果为1 说明没有在使用event_base之前调用此函数 可能会引起错误
        if (event_debug_created_threadable_ctx_) {
            event_errx(1, "evthread initialization must be called BEFORE anything else!");
        }
    }
#endif

    //传入的参数为空的话 直接将全局变量中的内容清空
    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;
    }

    //将条件变量的相关操作保存到全局变量中
    if (cbs->alloc_condition && cbs->free_condition &&
        cbs->signal_condition && cbs->wait_condition) 
    {
        memcpy(target, cbs, sizeof(evthread_cond_fns_));
    }

    //如果开启了lock debug模式    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;
}

//设置线程获得自己id的方式 保存到全局变量evthread_id_fn_中linux上用
//pthread_self()获得线程id
void evthread_set_id_callback(unsigned long (*id_fn)(void))
{
    evthread_id_fn_ = id_fn;
}

在上面介绍evthread_set_lock_callbacks()的时候我们最后留了一个函数event_global_setup_locks_没有介绍,接下来我们看看他是干什么的
//下面函数安装一些全局锁
int event_global_setup_locks_(const int enable_locks)
{
    //在编译libevent的时候没有使用--disable-debug-mode
#ifndef EVENT__DISABLE_DEBUG_MODE
    //根据前面锁对象的分配方式分配一个 锁对象存储到event_debug_map_lock_中 这个锁用于log中,在log开启后会有个hash表用于存放所有安装的
    //事件,此锁就是用于事件从hash中的删除和添加, 至于为啥要存储事件,此处不在展开
    EVTHREAD_SETUP_GLOBAL_LOCK(event_debug_map_lock_, 0);
#endif

    //分配一个全局锁对象 evsig_base_lock 用于添加删除删除信号事件
    if (evsig_global_setup_locks_(enable_locks) < 0)
        return -1;

    //对于linux此处直接返回0 
    if (evutil_global_setup_locks_(enable_locks) < 0)
        return -1;

    //对于linx初始化一个全局锁对象 用于随机数的获取
    if (evutil_secure_rng_global_setup_locks_(enable_locks) < 0)
        return -1;
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值