条件变量
之前的博客给了很多互斥量的例子,如果认真看完我的博客加自己手敲一遍,一定会对互斥量有一定的理解
也应该对并发(异步)的查询法有一定的了解,因为上一篇博客的令牌桶会在取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;
}