条件变量
条件变量的提出首先要涉及一个概念,就是生产者消费者模型:
生产者消费者的模型提出了三种关系,两种角色,一个场所
三种关系:
- 生产者之间的互斥关系
- 消费者之间的竞互斥关系
- 生产者和消费者之间互斥和同步关系(同一时刻只能有一个,要么在生产,要么在消费,这就是互斥关系,只能在生产者生产完了之后才能消费,这就是同步关系)
两个角色:一般是用进程或线程来承担生产者或消费者
一个场所:有效的内存区域。(如单链表,数组)
我们就可以把这个想象成生活中的超市供货商,超市,顾客的关系,超市供货商供货,超市是摆放货物的场所,然后用户就是消费的。
条件变量属于线程的一种同步的机制,条件变量与互斥锁一起使用,可以使得线程进行等待特定条件的发生。条件本身是由互斥量保护的,线程在改变条件状态之前首先会锁住互斥量。其他线程在获得互斥量之前不会察觉这种改变,因此互斥量锁定后才能计算条件。
和互斥锁一样,使用条件变量,同样首先进行初始化:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
和互斥锁的初始化一样,它也可以采用init或者是直接利用宏进行初始化。
条件变量本身就是依赖互斥锁的,条件本身是由互斥量保护的,线程在改变条件状态钱先要锁住互斥量,它是利用线程间共享的全局变量进行同步的一种机制。
我们使用pthread_cond_wait进行等待条件变量变为真,如果在规定的时间不能满足,就会生成一个返回错误码的变量。
- 1
- 2
- 3
- 1
- 2
- 3
把锁传递给wait函数,函数自动把等待条件的线程挂起,放入消费者等待队列,然后解锁挂起线程所占有的互斥锁,这个时候就可以去跑其他线程,然后当等待条件满足的时候,这个时候从等待队列中出来执行,获得刚才自己所占有的锁。
一个线程可以调用pthread_cond_wait在一个Condition Variable上阻塞等待,这个函数做以下三步操作:
1. 释放Mutex
2. 阻塞等待
3. 当被唤醒时,重新获得Mutex并返回
满足条件的时候可以使用函数pthread_cond_signal进行唤醒
- 1
- 2
- 3
- 1
- 2
- 3
这两个函数都是用来进行唤醒线程操作的,signal一次从消费者队列中至少唤醒一个线程,broad_cast能唤醒等待该条件的所有线程。
当然和mutex类似,条件变量也需要清除。
生产者消费者示例:
#include<stdio.h>
#include<assert.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
typedef struct Node
{
struct Node *next;
int val;
}Node;
void list_init(Node **phead)
{
assert(phead);
Node *temp = (Node*)malloc(sizeof(Node));
temp->next = NULL;
temp->val = 0;
*phead = temp;
}
void list_push(Node *phead,int _data) //头插
{
assert(phead);
Node *pM = (Node*)malloc(sizeof(Node));
if (pM)
{
pM->val = _data;
pM->next = phead->next;
phead->next = pM;
}
}
int empty(Node *phead)
{
return (phead->next == NULL ? 1: 0);
}
void list_print(Node *phead)
{
assert(phead);
if (empty(phead))
return ;
Node *pCur = phead->next;
while (pCur)
{
printf("%d->",pCur->val);
pCur = pCur->next;
}
printf("%s\n","NULL");
}
void list_pop(Node *phead,int *data) //头删
{
assert(phead);
if (empty(phead))
return ;
Node *ptemp = phead->next;
phead->next = ptemp->next;
*data = ptemp->val;
free(ptemp);
ptemp = NULL;
}
void list_destroy(Node *phead)
{
assert(phead);
while (!empty(phead))
{
int data;
list_pop(phead,&data);
}
}
//生产者线程
void* producer(void *arg)
{
Node *phead = (Node *)arg;
while (1)
{
pthread_mutex_lock(&mutex); //申请互斥锁
int data = rand()%100;
list_push(phead,data);
pthread_mutex_unlock(&mutex);//释放互斥锁
printf("prodecer sucess %d\n",data);
pthread_cond_signal(&cond); //以单播的方式通知拥有条件变量的另外一个线程,告诉消费者,生产者生产好了,可以消费了。
sleep(1);
}
return NULL;
}
//消费者线程
void* consumer(void *arg)
{
Node *phead = (Node *)arg;
while (1)
{
int data;
pthread_mutex_lock(&mutex);
if (empty(phead)) //如果没有资源可以消费了,则等待
{
pthread_cond_wait(&cond,&mutex); //这个函数调用一定是在拥有互斥锁的前提下.这个函数做三件事,第一:释放互斥锁,二,阻塞等待,三,唤醒的时候重新获得互斥锁。
}
list_pop(phead,&data);//有资源就消费
pthread_mutex_unlock(&mutex);
printf("consumer sucess %d\n",data);
}
return NULL;
}
int main()
{
Node *phead;
list_init(&phead);
pthread_t id1;
pthread_t id2;
pthread_create(&id1,NULL,producer,(void*)phead);
pthread_create(&id2,NULL,consumer,(void*)phead);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
信号量
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);//类似P操作
int sem_trywait(sem_t *sem);
int sem_post(sem_t *sem);//类似V操作
用数组模拟环形队列存储数据代码:
#include<stdio.h>
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<semaphore.h>
//二个规则,(1)消费者一定要等到生产才能消费,(2)生产者如果生产的快的话,不能套消费者一个圈
#define SIZE 64
sem_t blanks; //二个信号量
sem_t datas;
int buf[SIZE]; //用数组模拟一个环形队列。
void *productor(void *arg)
{
int i = 0; //i = 0表示生产的起始位置。
while (1)
{
// pthread_mutex_lock(&mutex_p);
sem_wait (&blanks);//生产者在生产之前要有格子资源。
int data = rand()%1234;
buf[i] = data;
printf("productor done...data:%d\n",data);
i++;
i%=SIZE;//i++总有超过数组的长度的时候,为了模拟环形队列,所以求模。
sleep(1); //生产的慢点
sem_post(&datas);//生产者生产完了,数据资源就多了一个。
// pthread_mutex_unlock(&mutex_p);
}
return NULL;
}
void *consummer(void *arg)
{
int i = 0;
while (1)
{
sem_wait(&datas); //消费者在消费之前要有消费资源。
int data = buf[i];
printf("consummer done...data:%d\n",data);
i++;
i%= SIZE;
sem_post(&blanks);//消费者消费完了格子资源就多了一个。
}
return NULL;
}
int main()
{
sem_init(&blanks,0,SIZE);
sem_init(&datas,0,0); //信号量的初始化要在多线程之前
pthread_t id1,id2;
pthread_create(&id1,NULL,productor,NULL);
pthread_create(&id2,NULL,consummer,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
sem_destroy(&blanks);
sem_destroy(&datas);
return 0;
}