多线程实现令牌桶算法(互斥量+条件变量)

本文介绍了如何通过条件变量实现并发任务的优化,避免了查询法的盲等,提升线程间的通信效率。讲解了`pthread_cond_t`的使用,包括初始化、广播、信号、等待和定时等待,并举例说明了在令牌桶算法中如何应用条件变量来减少阻塞。

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

条件变量

之前的博客给了很多互斥量的例子,如果认真看完我的博客加自己手敲一遍,一定会对互斥量有一定的理解
也应该对并发(异步)的查询法有一定的了解,因为上一篇博客的令牌桶会在取token函数那里不断的加锁查看,解锁等待
我们可以看出,查询法都是盲等,不停的查看状态,
所以,我们可以优化我们的底层逻辑,改成通知法,也就是条件变量

条件变量本身不是锁,但它也可以造成线程阻塞,通常和互斥量配合使用。给多线程提供一个会和的场所

两种初始化方式,静态和动态,静态是宏初始化,属性为默认

基本类型:pthread_cond_t类型

pthread_cond_init
参数:第一个为条件变量的的地址,第二个为属性
功能:动态初始化条件变量
返回值:int,成功返回0

pthread_cond_destroy
参数:pthread_cond_t类型变量的地址
功能:
返回值:

pthread_cond_broadcast
参数:pthread_cond_t类型变量的地址
功能:把所有的都叫醒
返回值:int,成功返回0

pthread_cond_signal
参数:pthread_cond_t类型变量的地址
功能:叫醒至少一个线程
返回值:int,成功返回0

pthread_cond_wait
参数:第一个为条件变量的的地址,第二个为初始化好的互斥量
功能:阻塞等待一个条件变量,相当于解锁等待,当有signal或者brodcast那么第一步是先抢锁,给当前所属线程上锁
返回值:int,成功返回0

pthread_cond_timedwait
参数:条件变量
功能:限时等待
返回值:int,成功返回0

本节相较于上一节代码变化部分:

结构体变化

struct mytbf_st
{
    int cps;
    int burst;
    int token;
    int pos;
    pthread_mutex_t mut;
    pthread_cond_t cond;
};

线程执行函数变化

static void *thr_alrm(void *p)
{
    while (1)
    {
        pthread_mutex_lock(&mut_job);
        for (int i = 0; i < MYTBF_MAX; i ++ )
        {
            if (job[i] != NULL)
            {
                pthread_mutex_lock(&job[i]->mut);
                job[i]->token += job[i]->cps;
                if(job[i]->token > job[i]->burst)
                    job[i]->token = job[i]->burst;
                pthread_cond_broadcast(&job[i]->cond);		//放token的时候就通知所有线程,已经有token了,可以拿了然后去干活了
                pthread_mutex_unlock(&job[i]->mut);
            }
        }
        pthread_mutex_unlock(&mut_job);
        sleep(1);
    }
}

初始化的函数部分

mytbf_t *mytbf_init(int cps, int burst)
{
    struct mytbf_st *me;
    int pos;

    pthread_once(&init_once, module_load);

    me = malloc(sizeof (*me));
    if (me == NULL)
        return NULL;

    me->token = 0;
    me->cps = cps;
    me->burst = burst;
    pthread_mutex_init(&me->mut, NULL);
    pthread_cond_init(&me->cond, NULL);

    pthread_mutex_lock(&mut_job);
    pos = get_free_pos_unlocked();
    if (pos < 0)
    {
        pthread_mutex_unlock(&mut_job);
        free(me);
        return NULL;
    }
	me->pos = pos;

    job[pos] = me;

    pthread_mutex_unlock(&mut_job);
    return me;
}

取token部分
cond_wait相当于解锁等待,等signal或者broadcast叫醒的时候第一时间抢锁,上锁,查看是否有token,没有就继续调用
pthread_cond_wait然后等待再一次被叫醒

int mytbf_fetchtoken(mytbf_t *ptr, int size)
{
    struct mytbf_st *me = ptr;
    int n;
    if (size <= 0)
        return -EINVAL;

    pthread_mutex_lock(&me->mut);
    while (me->token <= 0)
    {
        pthread_cond_wait(&me->cond, &me->mut);
        /*
        pthread_mutex_unlock(&me->mut);
        sched_yield();
        pthread_mutex_lock(&me->mut);
        */
    }

    n = min(me->token, size);

    me->token -= n;
    pthread_mutex_unlock(&me->mut);

    return n;
}

还token部分,有一种可能是两个线程刚好使用了同一个令牌桶,所以其中一个线程因为另一个线程拿了令牌而阻塞住
当那个线程归还令牌的时候有可能刚好满足当前线程的需要,所以要通知该线程,但是没办法知道是哪个线程,只能
broadcast,宁叫错,不放过

int mytbf_returntoken(mytbf_t *ptr, int size)
{
    struct mytbf_st *me = ptr;

    if (size <= 0)
    {
        return -EINVAL;
    }

    pthread_mutex_lock(&me->mut);
    me->token += size;
    if (me->token > me->burst)
    {
        me->token = me->burst;
    }

    pthread_cond_broadcast(&me->cond);
    pthread_mutex_unlock(&me->mut);

    return size;
}

销毁部分

int mytbf_destroy(mytbf_t *ptr)
{
    struct mytbf_st *me = ptr;

    pthread_mutex_lock(&mut_job);
    job[me->pos] = NULL;

    pthread_mutex_unlock(&mut_job);

    pthread_mutex_destroy(&me->mut);
    pthread_cond_destroy(&me->cond);

    free(ptr);

    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值