linux线程同步之条件变量condition

参考:https://blog.csdn.net/i_love_blog/article/details/72630121

条件变量使我们可以睡眠等待某种条件出现。
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
条件变量类型为 pthread_cond_t。
条件变量与互斥量一起使用的时候,允许线程以无竞争的方式等待特定的条件发生。
条件本身是由互斥量保护的。线程在改变条件变量状态前必须先锁住互斥量。

另一种是动态分配的条件变量,则用pthread_cond_init函数进行初始化。


在释放底层的内存空间之前,可以使用pthread_cond_destroy对条件变量进行去初始化。

条件变量在使用前必须初始化,一种是静态初始化:pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

相关API介绍:

1、  int pthread_cond_init(pthread_cond_t *restrict cond,
                                      const pthread_condattr_t *restrict attr);//用自定义属性初始化一个条件变量

       pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//用默认属性静态初始化一个条件变量

     int pthread_cond_destroy(pthread_cond_t *cond);//销毁一个条件变量

函数介绍:pthread_cond_destroy为销毁一个条件变量,“销毁”这个词其实不大贴切,本函数的真正效果是,使得条件变量cond变为未初始化的状态。下面的各种关于条件变量的API,是不允许使用“未初始化的条件变量”的,否则会导致不可预知的结果。一个被“销毁”的条件变量,可以被重新初始化。

在某时刻,如果没有任何线程被该条件变量cond堵塞,那么使用pthread_cond_destroy函数来“销毁”这个条件变量,是个安全操作,但是,如果如果某个线程正在堵塞地等待这个条件变量,这时如果“销毁”了这个cond,那么会引发不可预知的结果。

pthread_cond_init()函数的作用是,用属性attr来初始化条件变量cond,如果attr的实参为NULL,那么attr在本函数内部会自动被修改为默认属性。

只有cond变量本身可以被用作同步。换句话说,不允许使用cond变量的副本,传给这些api:pthread_cond_wait(),       pthread_cond_timedwait(), pthread_cond_signal(), pthread_cond_broadcast(), and pthread_cond_destroy() ,否则会导致不可预知的结果。

对于一个已经初始化过的条件变量,进行二次初始化,会导致不可预知的后果。

对于静态分配的条件变量,可以用PTHREAD_COND_INITIALIZER这个宏对它进行静态初始化,效果相当于用

pthread_cond_init(&cond, NULL)进行初始化。

返回值:成功则返回0,否则直接返回错误码,而不是设置errno

2、int pthread_cond_timedwait(pthread_cond_t *restrict cond,
                                            pthread_mutex_t *restrict mutex,
                                            const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,
                                  pthread_mutex_t *restrict mutex);
基础知识:一个线程使用该函数进入睡眠(如果等不到条件变量,需要睡眠的话)之前,会释放实参互斥量mutex。
函数介绍:这两个功能基本一样,唯一的区别在于,前者增加了时间参数。(本小节再提到本函数指的是这两个函数)

当条件变量为某个值时,该函数会堵塞。

扫描二维码关注公众号,回复: 2203875 查看本文章

该函数会原子性地释放互斥量(实参mutex),并且本线程会堵塞。这里所谓的“原子性地”的意思是:另一个线程总是原子性地先访问mutex再访问条件变量cond。也就是说:假设有一个线程A,即将被堵塞,A已经释放了互斥锁,这时线程B获得了这个互斥锁,那么线程B获取互斥锁之后,调用pthread_cond_broadcast()或pthread_cond_signal()的效果,就好像是在线程A被堵塞之后调用的似的(尽管此时A还没有被堵塞)。

一旦本函数成功返回,那么实参互斥锁mutex将会被锁住,也即,该mutex被本线程所持有,此时别的线程都无法获得该mutex。

当使用条件变量时,总是有一个这样的布尔值,这个值跟每一个条件变量相关,当这个布尔值true时,线程才会继续下去。线程可能从该函数中被假唤醒。由于从该函数中返回时,无法指示出关于这个布尔值的信息,所以,在本函数返回时,应重新判断一下这个布尔量的值。

这两个wait函数需要用到一个mutex和一个条件变量cond,这个mutex通过互斥来保护cond变量,如果并行的多个wait函数使用了不同mutex来保护cond,结果是不可预知的。也即,当等待条件变量cond时,这个cond必须与惟一的mutex上绑定,当条件变量成立(解除堵塞)后,才能结束这个动态绑定。

这两个函数都是“线程取消点”,关于取消点的概念,可参考另一篇博文《linux多线程相关的API-(3)--线程取消cancel与清理push/pop》

返回值:成功则返回0,失败则直接返回错误码,而不是设置errno。

这个函数pthread_cond_wait可以分解为这种形式,来帮助我们来理解它的工作过程:

int pthread_cond_wait(pthread_cond_t *restrict cond,  pthread_mutex_t *restrict mutex)
{
    pthread_mutex_unlock(mutex);//解锁,这样别的线程才可以可以修改cond
    wait(cond);//立即进入睡眠,直到别的线程执行了pthread_cond_signal或者pthread_cond_broadcast,才能执行下一行    
  pthread_mutex_lock(mutex);//加锁
}

由上述分解过程可知,在使用这两个API时,应在这两个函数前后用和pthread_mutex_unlock(mutex)给包起来:形如:

pthread_mutex_lock(&mutex);//加锁
while(myval != 3)
    pthread_cond_wait(&cond, &mutex);//本函数内部会解锁、加锁
pthread_mutex_unlock(&mutex);//解锁


 3、 int pthread_cond_broadcast(pthread_cond_t *cond);    

 int pthread_cond_signal(pthread_cond_t *cond);

pthread_cond_broadcast可以唤醒所有被cond条件变量堵塞的线程;
pthread_cond_signal可以唤醒至少一个被cond条件变量堵塞的线程(我认为应该是最多一个,但手册上写的就是最少一个,我不理解);

如果被cond堵塞的线程不止一个,那么这些线程的唤醒顺序取决于调度策略。当这两个函数唤醒那些被cond堵塞的线程时,被唤醒的线程将会从pthread_cond_wait、或者pthread_cond_timedwait函数中返回,此时被唤醒的线程就会把互斥锁带走(持有互斥锁mutex)。这些被唤醒的线程会按照调度策略来争夺互斥锁,好像是每一个被唤醒的线程都调用了pthread_mutex_lock这个函数似的。

不管某线程当前是否持有mutex,它都可以调用pthread_cond_signal、pthread_cond_broadcast。当然,如果你希望调度行为是可预测的、是可控的,那么你应当在调用pthread_cond_signal、pthread_cond_broadcast之前,把互斥锁锁住(持有这个互斥锁)。

如果没有任何线程被cond给堵塞,这时调用pthread_cond_signal、pthread_cond_broadcast,也没啥事,不会产生什么坏结果的。

返回值:成功则返回0,否则直接返回错误码,而不是设置errno。


应用实例:标准的使用步骤如下:

应用场景描述:定义一个所有线程均可读写的全局变量uint8 cmd,主线程实时修改cmd的值,
当cmd=1时,线程1可以运行,否则线程1睡眠;
当cmd=2时,线程2可以运行,否则线程2睡眠;


猜你喜欢

转载自blog.csdn.net/qq_31073871/article/details/81004641