线程的同步
1. 条件变量
提供线程之间的一种通知机制,当某一条件满足时,线程A可以通知阻塞在条件变量上的线程B,B所期望的条件已经满足,可以解除在条件变量上的阻塞操作,继续做其他事情。
我们需要这种机制,当互斥量被锁住以后发现当前线程还是无法完成自己的操作,那么它应该释放互斥量,让其他线程工作。
1、可以采用轮询的方式,不停的查询你需要的条件
2、让系统来帮你查询条件,使用条件变量pthread_cond_t cond
2. 条件变量的初始化和销毁
//条件变量使用之前需要初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
// 默认属性为空NULL
//条件变量使用完成之后需要销毁
int pthread_cond_destroy(pthread_cond_t *cond);
3. 条件变量的使用
//条件变量使用需要配合互斥量
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
//1、使用pthread_cond_wait等待条件变为真。传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的互斥量传递给函数。
//2、这个函数将线程放到等待条件的线程列表上,然后对互斥量进行解锁,这是个原子操作。当条件满足时这个函数返回,返回以后继续对互斥量加锁。
当条件满足的时候,需要唤醒等待条件的线程
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
1、pthread_cond_broadcast唤醒等待条件的所有线程
2、pthread_cond_signal至少唤醒等待条件的某一个线程
注意,一定要在条件改变以后在唤醒线程
4. 实例练习
创建两个线程,一个线程往buff里写数据,每隔1s写一次,总共写30次,一个线程往buff里读数据,每隔2s读一次,总共读30次:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#define BUFF_SIZE 5
typedef struct product
{
int buff[BUFF_SIZE];//数据buff
int readpos,writepos;//读写位置,即buff的下标
pthread_mutex_t lock;//互斥量,配合条件变量使用
pthread_cond_t notempty;//非空条件
pthread_cond_t notfull;//非满条件
}*Buff_t;
void put(Buff_t p,int data);
int get(Buff_t p);
//进行互斥量、条件变量、位置(下标)的初始化
void init_func(Buff_t p)
{
pthread_mutex_init(&(p->lock),NULL);//互斥量初始化
//条件变量初试化
pthread_cond_init(&(p->notempty),NULL);
pthread_cond_init(&(p->notfull),NULL);
//初始化时读和写的位置(下标)均为零
p->readpos=0;
p->writepos=0;
}
//函数功能:进行互斥量、条件变量的销毁
void destory(Buff_t p)
{
pthread_mutex_destroy(&p->lock);
pthread_cond_destroy(&p->notempty);
pthread_cond_destroy(&p->notfull);
p->readpos=0;
p->writepos=0;
}
void * producer(void *arg)//子线程生成,然后放入buff
{
int i;
for(i=1;i<=30;i++)
{
put((Buff_t)arg,i);
printf("put the %d production to buff\n",i);
sleep(1);
printf("put the %d production to buff success\n",i);
}
return NULL;
}
void *customer(void *arg)//子线程消费,行buff去出相关值并打印
{
int i,val;
for(i=1;i<=30;i++)
{
val=get((Buff_t)arg);
printf("get the production form buff\n");
sleep(2);
printf("get the production form buff success,val is %d\n",val);
}
return NULL;
}
void put(Buff_t p,int data)
{
pthread_mutex_lock(&p->lock);//进行加锁操作
if((p->writepos+1)%BUFF_SIZE == p->readpos)//判断buff是否满了
{
printf("producer wait for not full\n");
pthread_cond_wait(&p->notfull, &p->lock);//如果满了,就需要等待到非满的时候,等待时,互斥量会被解锁,其他有进行加锁的线程就可以运行(这里的等待时,get()函数就可运行),当条件满足时这个函数返回,返回以后继续对互斥量加锁。
}
p->buff[p->writepos] = data;//buff非满时,就可往buff里写入数据,
p->writepos = (p->writepos+1) % BUFF_SIZE;//写位置加一,当超过BUFF_SIZE时,要从头开始(即为零)
pthread_cond_signal(&p->notempty);//发出非空信号,唤醒等待条件的线程
pthread_mutex_unlock(&p->lock);//解锁操作
}
int get(Buff_t p)
{
int val;
pthread_mutex_lock(&p->lock);//进行加锁操作
if(p->readpos==p->writepos)//判断buff是否为空,为空就等待,直到为非空
{
printf("it is empty,wait\n");
pthread_cond_wait(&p->notempty,&p->lock);
}
val = p->buff[p->readpos];
p->readpos=(p->readpos+1)%BUFF_SIZE;
pthread_cond_signal(&p->notfull);
pthread_mutex_unlock(&p->lock);
return val;
}
int main()
{
Buff_t p=(Buff_t)malloc(sizeof(struct product));
pthread_t product_t,customer_t;//线程id
init_func(p); //在使用互斥量、条件变量前,调用初始化函数,进行相关初始化
//创建两个线程,线程producer往buff中每秒存值一次,存30次
//线程customer从buff中每两秒取值一次,并打印,取30次
pthread_create(&product_t, NULL, producer, (void *)p);
pthread_create(&customer_t, NULL, customer, (void *)p);
//连接线程,等待线程结束
pthread_join(product_t, NULL);
pthread_join(customer_t, NULL);
//销毁互斥量、条件变量
destory(p);
return 0;
}
运行结果: