Linux线程同步——条件变量

时间:2023-12-26 19:41:19

互斥锁是用来给资源上锁的,而条件变量是用来等待而不是用来上锁的。

条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。

通常条件变量和互斥锁同时使用。

和条件变量使用有关的几个重要函数:

int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞

1. 初始化:

条件变量采用的数据类型是pthread_cond_t, 在使用之前必须要进行初始化, 这包括两种方式:

  • 静态: 可以把常量PTHREAD_COND_INITIALIZER给静态分配的条件变量.
  • 动态: pthread_cond_init函数, 是释放动态条件变量的内存空间之前, 要用pthread_cond_destroy对其进行清理.

Linux线程同步——条件变量#include <pthread.h>
Linux线程同步——条件变量
Linux线程同步——条件变量int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr);
Linux线程同步——条件变量int pthread_cond_destroy(pthread_cond_t *cond);
Linux线程同步——条件变量
Linux线程同步——条件变量成功则返回0, 出错则返回错误编号.

当pthread_cond_init的attr参数为NULL时, 会创建一个默认属性的条件变量; 非默认情况以后讨论.

2. 等待条件:

Linux线程同步——条件变量#include <pthread.h>
Linux线程同步——条件变量
Linux线程同步——条件变量int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restric mutex);
Linux线程同步——条件变量int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout);
Linux线程同步——条件变量
Linux线程同步——条件变量成功则返回0, 出错则返回错误编号.

这两个函数分别是阻塞等待和超时等待.

等待条件函数等待条件变为真, 传递给pthread_cond_wait的互斥量对条件进行保护, 调用者把锁住的互斥量传递给函数. 函数把调用线程放到等待条件的线程列表上, 然后对互斥量解锁, 这两个操作是原子的. 这样便关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道, 这样线程就不会错过条件的任何变化.

当pthread_cond_wait返回时, 互斥量再次被锁住.

3. 通知条件:

Linux线程同步——条件变量#include <pthread.h>
Linux线程同步——条件变量
Linux线程同步——条件变量int pthread_cond_signal(pthread_cond_t *cond);
Linux线程同步——条件变量int pthread_cond_broadcast(pthread_cond_t *cond);
Linux线程同步——条件变量
Linux线程同步——条件变量成功则返回0, 出错则返回错误编号.

这两个函数用于通知线程条件已经满足. 调用这两个函数, 也称向线程或条件发送信号. 必须注意, 一定要在改变条件状态以后再给线程发送信号.

条件变量使用示例

下面是个非常简单的例子,很好的演示了条件变量如何使用。

我将创建一个生产者线程,两个消费者线程,通过互斥量和条件变量同步他们的生产和消费操作。

需要注意的一个地方是,pthread_cond_wait操作必须传入一个条件变量和互斥量,线程先对互斥量上锁,然后再执行pthread_cond_wait,pthread_cond_wait会将互斥量解锁。

pthread_cond_wait在while循环中而不在if中的原因是,“可能被意外唤醒”,所以需要在唤醒之后再检查缓冲区。

 #include <stdio.h>
#include <pthread.h>
#include <unistd.h> pthread_cond_t condc,condp;
pthread_mutex_t the_mutex; unsigned int buffer = ;
const int MAX = ; void *producer(void *ptr){
for(int i = ; i < MAX; i++){
pthread_mutex_lock(&the_mutex);
while(buffer != ) pthread_cond_wait(&condp, &the_mutex);
sleep();
buffer = i;
printf("producer pthread produce one production %d.\n", i);
pthread_cond_broadcast(&condc);//唤醒两个消费者线程
pthread_mutex_unlock(&the_mutex);
}
pthread_exit();
} void *consumer1(void *ptr){
for(int i = ; i < MAX; i++){
pthread_mutex_lock(&the_mutex);
while(buffer == ) pthread_cond_wait(&condc, &the_mutex);
printf("consumer1 pthread consume one production %d.\n", buffer);
buffer = ;
pthread_cond_signal(&condp);
pthread_mutex_unlock(&the_mutex);
}
pthread_exit();
}
void *consumer2(void *ptr){
for(int i = ; i < MAX; i++){
pthread_mutex_lock(&the_mutex);
while(buffer == ) pthread_cond_wait(&condc, &the_mutex);
printf("consumer2 pthread consume one production %d.\n", buffer);
buffer = ;
pthread_cond_signal(&condp);
pthread_mutex_unlock(&the_mutex);
}
pthread_exit();
} int main(void){
pthread_t pro, con1, con2;
pthread_mutex_init(&the_mutex,);
pthread_cond_init(&condc,);
pthread_cond_init(&condp,);
pthread_create(&con1, , consumer1, );
pthread_create(&pro, , producer, );
pthread_create(&con2, , consumer2, );
pthread_join(pro, );
pthread_join(con1, );
pthread_join(con2, );
pthread_cond_destroy(&condc);
pthread_cond_destroy(&condp);
pthread_mutex_destroy(&the_mutex);
return ;
}

运行结果

Linux线程同步——条件变量