生产者与消费者模型
模型:生产者与消费者模型由消费者,生产者以及交易场所构成。
两个线程同时访问一块临界区域的时候,一个进程往这块区域中写数据,另一个进程在里面读数据。被访问的这块临界区域通常叫缓冲区,而往这块缓冲区里写数据的叫生产者,在这块缓冲区里读数据的叫消费者。
遵循的原则: 要实现消费者与生产者的关系,要满足一个原则,就是“321“原则。 3代表的是有三个关系、2代表的是两种角色、1就是一个交易场所。
3种关系:“消费者——消费者,生产者——生产者,消费者——生产者”关系。
其中生产者与生产者存在互斥关系、消费者与消费者之间存在互斥关系、生产者与消费者之间存在同步与互斥关系。
2个角色:“生产者,消费者”。
1个交易场所:“缓冲区”
基于单链表的生产者与消费者模型
互斥锁:如果生产者往缓冲区里生产了一部分数据时,消费者就来直接读取了,那么消费者读到的数据和生产者生产的数据其实不是一个,也就是说生产者与消费者之间没有互斥的制约,为了保证读取到的数据的完整性,必须引入互斥机制,即互斥锁。
int pthread_mutex_lock(pthread_mutex_t *mutex);//上锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁
互斥有两种等待方式,非阻塞时等待(pthread_mutex_lock),和阻塞时等待(pthread_mutex_trylock),解锁为(pthread_mutex_destroy)
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);//由time控制的等待
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);//阻塞式等待
当生产者的生产速度特别慢时,而消费者的读取速度特别快,这时当消费者把缓冲区里的数据读完时,生产者还没有生产出相应的数据,那么消费者就要在这里申请互斥锁来继续访问这块缓冲区,为了让消费者在读到缓冲区里没数据的时候,就挂起等待,因此引入一个概念就是条件变量。
条件变量:
条件变量是用来判断生产者与消费者是否满足同步性的。条件变量使我们可以睡眠等待某种条件出现。
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立”(给出条件成立信号)。相当于消费者在等待生产者的时候,当生产者生产出来数据以后, 通过条件变量的信号去通知消费者,然后消费者被唤醒继续消费。
为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。条件变量类型为pthread_cond_t。
测试用例:
由一个单向链表实现消费模型:在一个单链表里用(单)生产者与(单)消费者模型来模拟生产、消费情景。
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<malloc.h>
pthread_mutex_t mutex_lock=PTHREAD_MUTEX_INITIALIZER;//init lock
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;//init cond
ypedef struct SeqList
{
int data;
struct SeqList* next;
}SeqList;
SeqList* list=NULL;
SeqList* Buynode(int d)
{
SeqList* list=(SeqList*)malloc(sizeof(SeqList));
list->data=d;
list->next=NULL;
return list;
}
void PushFront(SeqList **pplist,int d)
{
SeqList* tmp=Buynode(d);
tmp->next=*pplist;
*pplist=tmp;
}
int PopFront(SeqList** pplist)
{
if(*pplist==NULL)
{
return ;
}
SeqList* tmp=(*pplist)->next;
int ret=(*pplist)->data;
free(*pplist);
*pplist=tmp;
return ret;
}
void *product(void *arg)
{
while(1)
{
int d=rand()/9527;
int i=pthread_mutex_lock(&mutex_lock);//申请锁
if(i!=0)
{
pthread_cond_wait(&cond,&mutex_lock);
}
PushFront(&list,d);
printf("product %d\n",d);
pthread_mutex_unlock(&mutex_lock);//释放锁
sleep(1);
}
}
void *consume(void *arg)
{
while(1)
{
sleep(1);
int i=pthread_mutex_lock(&mutex_lock);
int ret=PopFront(&list);
if(i!=0)
{
pthread_cond_wait(&cond,&mutex_lock);
}
printf("consume %d\n",ret);
pthread_mutex_unlock(&mutex_lock);
}
}
int main()
{
pthread_t t_a;
pthread_t t_b;
pthread_create(&t_a,NULL,product,(void*)NULL);
pthread_create(&t_b,NULL,consume,(void*)NULL);
pthread_join(t_b,NULL);
pthread_mutex_destroy(&mutex_lock);
pthread_cond_destroy(&cond);
return 0;
}
基于环形队列的生产者与消费者模型
格子数信号量由c通知p,消费后,格子为空,可写入。
数据信号量由p通知c,生产后,格子有数据,可读取。
实现条件:
- p不能超过c一圈。除了开始位置,c和p不能相遇,如果p写满数据来到c后面时只能等c拿走数据后再写入。
c永远跟在p后面。p没有写数据时,c不能读取数据。
测试用例:
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>
#define SIZE 4
int arr[SIZE]={0};
int ret = 0;
sem_t datasem;
sem_t blanksem;
void *product(void *arg)
{
int i=0;
while(1)
{
sem_wait(&blanksem);
arr[ret]=i;
printf("product done! %d\n", arr[ret]);
sem_post(&datasem);
i++;
ret++;
ret %= SIZE;
}
}
void *consume(void *arg)
{
while(1)
{
sem_wait(&datasem);
printf("consume done! %d\n", arr[ret]);
sem_post(&blanksem);
sleep(1);
}
}
int main()
{
pthread_t c;
pthread_t p;
sem_init(&datasem, 0, 0);
sem_init(&blanksem,0, SIZE);
pthread_create(&p, NULL, product, NULL);
pthread_create(&c, NULL, consume, NULL);
pthread_join(c, NULL);
pthread_join(p, NULL);
sem_destroy(&datasem);
sem_destroy(&blanksem);
return 0;
}