Linux下生产者-消费者模型的两种实现方式
本文主要来总结生产者-消费者模型的代码实现,至于其原理,请大家自行百度.
一、基于链表的生产-消费模型(条件变量)
我们以链表为例,生产者进行头部插入,消费者进行头部删除,因此,先将链表相关操作封装为LinkList.h,具体代码如下:
////////////////////////////////////
//文件说明:LinkList.h
//作者:高小调
//创建时间:2017年06月27日 星期二 14时57分27秒
//开发环境:Kali Linux/g++ v6.3.0
////////////////////////////////////
#include<assert.h>
#include<stdlib.h>
#include<stdio.h>
//链表节点
typedef struct LinkListNode{
int val;
struct inkListNode *next;
}Node,*pNode,**ppNode;
//初始化链表
void InitLinkList(ppNode head){
assert(head);
*head = NULL;
}
//判断链表是否为空
int IsEmpty(pNode head){
return head==NULL;
}
//申请新节点
pNode BuyNewNode(int val){
pNode ret = (pNode)malloc(sizeof(Node));
ret->val = val;
ret->next = NULL;
return ret;
}
//头插
void PushFront(ppNode head,int val){
assert(head);
if(*head==NULL){
*head = BuyNewNode(val);
return ;
}
pNode newNode = BuyNewNode(val);
newNode->next = *head;
*head = newNode;
}
//头删
void PopFront(ppNode head,int *val){
assert(head);
if((*head) == NULL){
return ;
}
if((*head)->next == NULL){
*val = (*head)->val;
free(*head);
*head = NULL;
return ;
}
pNode del = *head;
*head = del->next;
*val = del->val;
free(del);
}
//销毁链表
void Destory(ppNode head){
assert(head);
pNode cur = *head;
pNode del = NULL;
while(cur!=NULL){
del = cur;
cur = cur->next;
free(del);
}
*head = NULL;
}
//打印链表
void PrintLinkList(pNode head){
pNode cur = head;
while(cur!=NULL){
printf("%d ",cur->val);
cur = cur->next;
}
printf("\n");
}
然后进入我们线程的生产消费模型:
////////////////////////////////////
//文件说明:test.c
//作者:高小调
//创建时间:2017年06月27日 星期二 14时56分13秒
//开发环境:Kali Linux/g++ v6.3.0
////////////////////////////////////
#include<stdio.h>
#include<pthread.h>
#include"LinkList.h"
//互斥锁
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
//条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//测试链表
void TestLinkList(){
pNode head;
InitLinkList(&head);
int tmp;
for(int i=0; i<10; ++i){
PushFront(&head,i);
PrintLinkList(head);
}
for(int i=0; i<10; ++i){
PopFront(&head,&tmp);
PrintLinkList(head);
}
}
pNode head;
//生产者:每次向头节点插入数据
void *Productor(void*arg){
int val = 0;
while(1){
//互斥锁加锁:确保生产时不会消费,消费时不会生产
pthread_mutex_lock(&lock);
val = rand()%100;
PushFront(&head,val);
printf("Productor push %d\n",val);
//互斥锁解锁
pthread_mutex_unlock(&lock);
//条件变量,生产完成之后向消费者发出信号消费
pthread_cond_signal(&cond);
sleep(1);
}
}
//消费者:每次将头节点数据取出
void *Consumer(void*arg){
int val = 0;
while(1){
//互斥锁
pthread_mutex_lock(&lock);
while(head==NULL){
//链表中没数据,阻塞等待生产者发消费信号
printf("wait for data\n");
pthread_cond_wait(&cond,&lock);
}
PopFront(&head,&val);
printf("Consumer pop %d\n",val);
pthread_mutex_unlock(&lock);
sleep(1);
}
}
int main(){
InitLinkList(&head);
pthread_t cid1,cid2;
pthread_create(&cid1,NULL,Productor,NULL);
pthread_create(&cid2,NULL,Consumer,NULL);
pthread_join(&cid1,NULL);
pthread_join(&cid2,NULL);
return 0;
}
二、基于环形队列的生产-消费模型(信号量)
////////////////////////////////////
//文件说明:test2.c
//作者:高小调
//创建时间:2017年06月27日 星期二 16时29分30秒
//开发环境:Kali Linux/g++ v6.3.0
////////////////////////////////////
#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
#include<stdlib.h>
#define SIZE 1024
//环形队列
int arr[SIZE] = {0};
sem_t sem_pro; //描述环形队列中的空位置
sem_t sem_con; //描述唤醒队列中的数据
//生产者,只要环形队列有空位,便不断生产
void*productor(void*arg){
int data = 0;
int proIndex = 0;
while(1){
//有空位便生产,没空位便阻塞等消费者消费
sem_wait(&sem_pro);
data = rand()%1234;
arr[proIndex] = data;
printf("product done %d\n",data);
proIndex = (proIndex+1)%SIZE;
//供消费者消费的数据加1
sem_post(&sem_con);
}
}
//消费者,只要环形队列中有数据,就不断消费
void*consumer(void*arg){
int data = 0;
int conIndex = 0;
while(1){
//环形队列中存在数据则消费,不存在数据则阻塞,直到有数据为止
sem_wait(&sem_con);
data = arr[conIndex];
printf("consume done %d\n",data);
conIndex = (conIndex+1)%SIZE;
//最后,消费了一个数据,空位加1
sem_post(&sem_pro);
}
}
int main(){
pthread_t pro,con;
sem_init(&sem_pro,0,SIZE-1); //一开始有很多空位置
sem_init(&sem_con,0,0); //但并没有数据
pthread_create(&pro,NULL,productor,NULL);
pthread_create(&con,NULL,consumer,NULL);
pthread_join(pro,NULL);
pthread_join(con,NULL);
sem_destroy(&sem_pro);
sem_destroy(&sem_con);
return 0;
}