同步保证了各个执行流对临界资源访问的合理性
当有资源的时候可以直接获得资源,没有资源进行线程等待,等待另一个线程生产资源,然后通知等待线程。
条件变量:
本质= 两个接口(等待接口+唤醒接口) + PCB等待队列
接口:
1.定义条件变量
pthread_cond_t
2.初始化:
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrictattr);
3.销毁
int pthread_cond_destroy(pthread_cond_t *cond);
4.等待接口(下面该接口重点分析)
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
5.唤醒接口
//唤醒PCB等待队列所有执行流
int pthread_cond_broadcast(pthread_cond_t *cond);
//唤醒PCB等待队列至少一个执行流
int pthread_cond_signal(pthread_cond_t *cond);
示例:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#define THREADCOUNT 2
//0代表当前碗里没有面
//1代表碗里有面
int g_noodle = 0;
pthread_mutex_t lock;
pthread_cond_t consume_cond;
pthread_cond_t product_cond;
void* ConsumeStart(void* arg)
{
(void)arg;
while(1)
{
pthread_mutex_lock(&lock);
while(g_noodle == 0)
{
//阻塞等
//1.将该PCB放到PCB等待队列当中去
//2.解锁
//3.等待被唤醒
pthread_cond_wait(&consume_cond, &lock);
}
g_noodle--;
printf("i am Consumer i Consume %d\n", g_noodle);
pthread_mutex_unlock(&lock);
//通知厨子,有可能在唤醒之后,还是厨子拿到了互斥锁,判断了碗里还是有面的,所以厨子被放到了PCB等待队列当中去了,所以说我们需要吃碗面的时候,通知一下PCB等待队列当中的线程
pthread_cond_signal(&product_cond);
}
return NULL;
}
void* ProductStart(void* arg)
{
(void)arg;
while(1)
{
pthread_mutex_lock(&lock);
while(g_noodle == 1)
{
pthread_cond_wait(&product_cond, &lock);
}
g_noodle++;
printf("i am Producer i product %d\n", g_noodle);
pthread_mutex_unlock(&lock);
pthread_cond_signal(&consume_cond);
}
return NULL;
}
int main()
{
pthread_mutex_init(&lock, NULL);
pthread_cond_init(&consume_cond, NULL);
pthread_cond_init(&product_cond, NULL);
pthread_t Consume_tid[THREADCOUNT];
pthread_t Product_tid[THREADCOUNT];
//pthread_t tid[2];
int i = 0;
int ret = -1;
for(; i < THREADCOUNT; i++)
{
ret = pthread_create(&Consume_tid[i], NULL, ConsumeStart, NULL);
if(ret < 0)
{
perror("pthread_create");
return 0;
}
}
for(i = 0; i < THREADCOUNT; i++)
{
ret = pthread_create(&Product_tid[i], NULL, ProductStart, NULL);
if(ret < 0)
{
perror("pthread_create");
return 0;
}
}
for(int i = 0; i < THREADCOUNT; i++)
{
pthread_join(Consume_tid[i], NULL);
pthread_join(Product_tid[i], NULL);
}
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&consume_cond);
pthread_cond_destroy(&product_cond);
return 0;
}
等待接口:
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
1.为什么要使用互斥锁
因为同步只是保证各个执行流对临界资源访问的合理性,并没有保证各个执行流对临界资源访问的互斥属性。
所以需要使用当前条件变量条件变量和互斥锁同时使用,保证同步+互斥
2.该接口内部逻辑实现
2.1.将调用者的PCB放到PCB等待队列当中
2.2.对互斥锁进行解锁
2.3.等待被唤醒
2.3.1.如果被其他执行流唤醒,需要移出PCB等待队列
2.3.2.对互斥锁进行加锁
2.3.2.1.加锁成功,pthread_cond_wait返回
2.3.2.2.加锁失败阻塞在pthread_cond_wait接口内部加互斥锁的逻辑中
2.3.2.2.1.拥有CPU资源继续加锁操作,知道加锁成功pthread_cond_wait返回
2.3.2.2.2.时间片到了,被迫让出CPU资源,在程序计数器和上下文信息中保存当前抢锁的指令和变量的值
3.pthread_cond_wait为什么要先将调用者的PCB放到PCB等待队列才进行释放互斥锁
防止消费线程提前释放互斥锁,生产线程抢在消费线程进行PCB等待之前抢到了所资源,并执行生成逻辑后,而这个时候将
消费线程放到PCB等待队列当中,就可能造成没有生产线程去调用pthread_cond_broadcast/signal(pthread_cond_t *cond)这样
的接口来唤醒PCB等待队列了。