线程同步---条件变量
互斥锁的缺点是它只有两种状态:锁定和非锁定
条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补互斥锁的不足。
条件变量内部是一个等待队列,放置等待的线程,线程在条件变量上等待和通知,互斥锁用来保护等待队列(对等待队列上锁),条件变量通常和互斥锁一起使用
条件变量允许线程等待特定条件发生,当条件不满足时,线程通常先进入阻塞状态,等待条件发生变化,一旦其他的某个线程改变了条件,可唤醒一个或多个阻塞的线程。
具体的判断条件还需用户给出
条件变量数据类型
pthread_cond_t
条件变量创建和销毁
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
返回值:成功返回0,失败返回错误编号
参数:
cond:条件变量
attr
:条件变量属性
条件变量等待操作
条件变量等待操作
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
int pthread_cond_timewait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict timeout);
返回值:成功返回0,出错返回错误编号
struct timesped{
time_t tv_sec; //seconds
long tv_nsec; //nanoseconds
};
参数:
cond:条件变量
mutex:互斥锁
注意:互斥锁mutex是对条件变量cond的保护,线程由于调用wait函数阻塞,否则释放互斥锁
条件变量通知操作
#include <pthread.h>
int pthread_cond signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
返回值:成功返回0,出错返回错误编号
参数:cond:条件变量
当条件满足时,线程需要通知等待的线程。
pthread_cond_signal 函数通知单个线程
pthread_cond_broadcast函数通知所有线程
例子:
一个线程负责计算结果,一个线程负责获取结果,计算结果的线程如果没有计算完毕,获取结果的线程则等待阻塞,计算结果的线程计算完毕在去通知(唤醒)获取结果的线程
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int res;
int is_wait; //用户给出用于判断的条件
pthread_cond_t cond; //条件变量
pthread_mutex_t mutex; //互斥锁
}Result;
//计算并将结果放置Result中的线程运行函数
void *set_fn(void *arg)
{
int i =1,sum = 0;
for(;i<=100;i++){
sum +=i;
}
Result *r = (Result *)arg;
//将计算结果放置到Result的res中
r->res = sum;
pthread_mutex_lock(&r->mutex);
//判断获取结果的现成是否准备好
while(!r->is_wait){
pthread_mutex_unlock(&r->mutex);//避免set_fn线程比gei_fn线程先运行出现死锁
usleep(100);
pthread_mutex_lock(&r->mutex);
}
pthread_mutex_unlock(&r->mutex);
//通知唤醒等待那个获取结果的线程
pthread_cond_broadcast(&r->cond);
return (void *)0;
}
//获得结果的线程运行函数
void *get_fn(void *arg)
{
Result *r = (Result *)arg;
//对两个线程共享的判断条件进行保护(加锁)
//两个线程对判断条件操作是互斥的
pthread_mutex_lock(&r->mutex);
//代表获取结果的线程已准备好
r->is_wait = 1;
//获取结果线程进行等待
pthread_cond_wait(&r->cond,&r->mutex);//等待
pthreda_mutex_unlock(&r->mutex);
//获取计算结果
int res = r->res;
printf("0x%lx get sum is %d\n",pthread_self(),res);;
return (void *)0;
}
int main(void)
{
int err;
pthread_t cal,get;
Result r;
r.is_wait = 0;
pthread_cond_init(&r.cond,NULL);
pthread_mutex_init(&r.mutex,NULL);
//启动获取结果的线程
if((err = pthread_create(&get,NULL,get_fn,(void*)&r)) != 0){
perror("pthread create error");
}
//启动计算结果的线程
if((err = pthread_create(&cal,NULL,set_fn,(void*)&r)) != 0){
perror("pthread create error");
}
pthread_join(cal,NULL);
pthread_join(get,NULL);
pthread_cond_destroy(&r.cond);
pthread_mutex_destroy(&r.mutex);
return 0;
}
编译:gcc -lpthread **.c -o test
运行:./test
结果:0x1566416 get sum 5050
pthread_cond_wait()函数内部流程{
1) unlock(&mutex);
2) lock(&mutex);//对等待队列进行上锁
3) 将线程自己插入到条件变量的等待队列中
4) unlock(&mutex);
5) 当前等待的线程阻塞 <==== 其他线程通知唤醒(broadcast signal)
6) 在唤醒后,lock(&mutex);
7) 从等待队列中删除线程自己
}
pthread_cond_wait前的lock和后的unlock不是一对