Linux C 编程中的条件变量与互斥锁
条件变量
这里先不引入条件变量的应用场景,我们先来了解什么是条件变量以及条件变量可以实现什么样的功能。
使用man
手册查看关于条件变量的信息,如下:
A condition (short for ``condition variable’’) is a synchronization device that allows threads to suspend execution and relinquish the processors until some predicate on shared data is satisfied. The basic operations on conditions are: signal the condition (when the predicate becomes true), and wait for the condition, suspending the thread execution until another thread signals the condition.
条件变量是一种允许线程在资源不满足的情况下挂起等待、退出执行的同步机制,对于条件变量常用到的操作即发送信号和等待信号。因此,条件变量实现的功能就是:条件不成立时你要挂起等待,条件成立时会通知你继续执行。
为什么需要条件变量及条件变量应用场景是什么?
编程时常遇到这样一种场景,线程A在创建后执行的过程中遇到向线程B请求信息的需求,但是此时线程A不知道线程B何时才能向其提供所需要的信息,倘若线程A一直轮询,则会消耗不必要的CPU资源;若线程A每隔一段时间再来询问,此时又可能因为线程B早已准备好信息却未被使用而降低了程序执行的效率。
因此,就需要一种机制来应对上述可能出现的问题,这种机制就是在线程A和线程B间定义一个条件变量,当线程A资源未满足时,停止等待来自线程B的信号,线程B的资源准备好后就会向线程A发送信号,此时线程A收到信号后会退出等待继续执行。这里条件变量就使线程A阻塞等待条件的出现,既减少了占用CPU的时间,又提高了程序运行的效率。
头文件#include <pthread.h>
中定义了信号变量及相关的操作:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t
类型的变量可以通过PTHREAD_COND_INITIALIZER
常量进行静态初始化。
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
pthread_cond_init
函数可以用来对环境变量cond
进行初始化,这里的cond_attr
是用来指定条件属性的,如果值为NULL
,表示使用默认属性,在LinuxThread
中也可以省略不写。
int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_signal
用于向所有与cond
条件变量绑定的等待线程发送信号,如果没有相关的线程在等待则不执行任何操作,若等待的线程有多个,则会选择其中一个唤醒。
int pthread_cond_broadcast(pthread_cond_t *cond);
pthread_cond_broadcast
顾名思义,将会唤醒所有与cond
相关联的等待线程。
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
pthread_cond_wait
用来完成释放互斥锁和等待来自条件变量cond
的信号的原子操作。当线程的资源不被满足时,将会被挂起(此时不占用CPU时间)直到条件满足才会继续执行,执行线程进行信号等待前必须亲自上锁,这里的锁即mutex
互斥锁(下文会提到),执行线程上锁后意味着该线程抢占到了等待信号的资格,此时该线程会被加载到信号等待队列,在这个过程中mutex
锁一直保持锁定状态,但是又会在线程挂起真正开始等待前释放锁,之后执行线程会进入阻塞状态直到条件满足,收到信号后的执行线程完成pthread_cond_wait
操作,此时互斥锁会被重新锁定。(千万别😵,提示一下,互斥锁锁住的是临界资源)
pthread_cond_wait
中包含的释放锁和挂起等待信号的操作必须是原子性的,不能分开,因为如果存在多个线程一直在释放信号前请求抢占互斥锁,原子操作可以保证条件变量不会在执行线程上锁和等待接收信号前被发送。(通俗点儿,举个例子,A放下水桶开始接水,接完水提起水桶,这个过程是必须连续的,如果不连续完成,很有可能存在这样的情况,A放下水桶,还没来的及接水,水桶被B拿去用了(因为很多人在等着打水)……)
所以说pthread_cond_wait
函数中包含cond
和mutex
两个参数,是为了接续完成开锁、收消息、上锁的动作,看这里,先做的是开锁,所以程序中实现时一定要保证先上个锁再使用pthread_cond_wait
函数,不然开谁的锁????
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,