生产者-消费者模型的两种实现方式

https://www.cnblogs.com/caolicangzhu/p/7086176.html

本文主要来总结生产者-消费者模型的代码实现,至于其原理,请大家自行百度.

一、基于链表的生产-消费模型(条件变量)

  我们以链表为例,生产者进行头部插入,消费者进行头部删除,因此,先将链表相关操作封装为LinkList.h,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//文件说明: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");
}

   然后进入我们线程的生产消费模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//文件说明: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;
}

 二、基于环形队列的生产-消费模型(信号量)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//文件说明: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;
}

 


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383718.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux系统【一】CPU+MMU+fork函数创建进程

切板中的内容输出到文件### 进程相关概念 程序&#xff1a;编译好的二进制文件&#xff0c;在磁盘上&#xff0c;不占用系统资源&#xff08;不包括磁盘&#xff09;。&#xff08;剧本&#xff09; 进程&#xff1a;占用系统资源&#xff0c;是程序的一次运行。&#xff08;戏…

Ubuntu卸载软件

用过使用dpkg软件管理工具得到所有已经安装的软件&#xff0c;如果不清楚软件的全名可以使用grep命令进行查找 然后再使用sudo apt-get remove --purge 软件名卸载软件&#xff08;--purge参数会删除配置文件&#xff0c;删的干净一些&#xff09; 例如&#xff1a;

一个重要且实用的signal---SIGCHLD

https://blog.csdn.net/lyztyycode/article/details/78150805SIGCHLD(修改)因为笔者之前的文章里面有错误&#xff0c;今天发现&#xff0c;立马做个修改。在下面我的一段关于sigchld信号相对于直接调用wait函数的好处时&#xff0c;我说调用wait函数要一直检测子进程是否执行完…

数据结构实验之链表七:单链表中重复元素的删除

https://blog.csdn.net/blessingxry/article/details/794455111.知识点&#xff1a;逆序建立链表&#xff0b;节点删除 2.题意&#xff1a;按照数据输入的相反顺序&#xff08;逆位序&#xff09;建立一个单链表&#xff0c;并将单链表中重复的元素删除&#xff08;值相同的元素…

Python3函数和代码复用

函数的定义 def 函数名([参数列表]):注释函数体注意事项 函数形参不需要声明类型&#xff0c;可以使用return语句在结束函数执行的同时返回任意类型的值&#xff0c;函数返回值类型与return语句返回表达式i的类型一致 即使该函数不需要接受任何参数&#xff0c;也必须保留一堆…

一文说尽C++赋值运算符重载函数(operator=)

http://www.cnblogs.com/zpcdbky/p/5027481.html在前面&#xff1a;关于C的赋值运算符重载函数(operator)&#xff0c;网络以及各种教材上都有很多介绍&#xff0c;但可惜的是&#xff0c;内容大多雷同且不全面。面对这一局面&#xff0c;在下在整合各种资源及融入个人理解的基…

Python a和a[:]的区别

简单来讲a[:]是深复制&#xff0c;a是浅复制&#xff0c;相当于赋值a的话是赋值了指针&#xff0c;赋值a[:]相当于复制了a对应的那段空间 例如&#xff1a; a [1,1,1,1,1,1]for x in a:if x1:a.remove(x)print(a)运行结果&#xff1a; remove操作是移除序列中第一个x元素。…

约瑟夫环(c语言程序完整版)

https://blog.csdn.net/m_hahahaha1994/article/details/51742453约瑟夫环&#xff08;约瑟夫问题&#xff09;是一个数学的应用问题&#xff1a;已知n个人&#xff08;以编号1&#xff0c;2&#xff0c;3…n分别表示&#xff09;围坐在一张圆桌周围。从编号为k的人开始报数&am…

Linux系统【二】exec族函数及应用

文件描述符 文件描述符表是一个指针数组&#xff0c;文件描述符是一个整数。 文件描述符表对应的指针是一个结构体&#xff0c;名字为file_struct&#xff0c;里面保存的是已经打开文件的信息 需要注意的是父子进程之间读时共享&#xff0c;写时复制的原则是针对物理地址而言…

白话C++系列(27) -- RTTI:运行时类型识别

http://www.cnblogs.com/kkdd-2013/p/5601783.htmlRTTI—运行时类型识别 RTTI&#xff1a;Run-Time Type Identification。 那么RTTI如何来体现呢&#xff1f;这就要涉及到typeid和dynamic_cast这两个知识点了。为了更好的去理解&#xff0c;那么我们就通过一个例子来说明。这个…

使用头文件的原因和规范

原因 通过头文件来调用库功能。在很多场合&#xff0c;源代码不便&#xff08;或不准&#xff09;向用户公布&#xff0c;只 要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库 功能&#xff0c;而不必关心接口怎么实现的。编译器会从库中提取相应…

转圈踢人问题

https://www.cnblogs.com/lanxuezaipiao/p/3339603.html 有N个人围一圈依次报数&#xff0c;数到3的倍数的人出列&#xff0c;问当只剩一个人时他原来的位子在哪里&#xff1f; 解答&#xff1a;经典的转圈踢人问题&#xff0c;好吧专业一点&#xff0c;约瑟夫环问题&#xff0…

Linux系统【三】回收子进程

孤儿进程 父进程先于子进程结束&#xff0c;则子进程成为孤儿进程&#xff0c;子进程的父进程成为init进程&#xff0c;则称init进程领养孤儿进程。现在好像是用户进程中的system进程。 僵尸进程 进程终止&#xff0c;父进程不进行回收&#xff0c;自己成残留资源(PCB)存放在…

string类的基本实现

https://blog.csdn.net/qq_29503203/article/details/52265829在面试中面试官常常会让你写出string类的基本操作&#xff0c;比如&#xff1a;构造函数&#xff0c;析构函数&#xff0c;拷贝构造等等.下面是除此之外的一些操作&#xff0c;希望可以帮助你更好的理解string以便以…

Python3常用数据结构

Python3中有三种组合数据类型&#xff0c;分别为&#xff1a; 序列类型&#xff1a;字符串&#xff08;str&#xff09;、元组&#xff08;tuple&#xff09;、列表&#xff08;list&#xff09;集合类型&#xff1a;集合&#xff08;set&#xff09;映射类型&#xff1a;字典…

Linux C++ 回射服务器

http://blog.csdn.net/qq_25425023/article/details/53914820回射服务器就是服务端将客户端的数据发送回去。我实现的回射服务器返回增加了时间。服务端代码&#xff0c;可以很容易看懂&#xff1a;[cpp] view plaincopy#include <sys/socket.h> #include <stdio.h&g…

TCP第四次挥手为什么要等待2MSL

当客户端进入TIME-WAIT状态的时候(也就是第四次挥手的时候)&#xff0c;必须经过时间计数器设置的时间2MSL(最长报文段寿命)后&#xff0c;才能进入关闭状态&#xff0c;这时为什么呢&#xff1f;&#xff1f;&#xff1f; 这最主要是因为两个理由&#xff1a; 1、为了保证客户…

计算机网络【一】概述+OSI参考模型

网络概述 局域网:覆盖范围小(100m以内)&#xff0c;自己花钱买设备&#xff0c;带宽固定(10M,100M,1000M)&#xff0c;自己维护&#xff08;接入层交换机直接连接电脑、汇聚层交换机直接连接接入层交换机&#xff09; 广域网:距离远&#xff0c;花钱买服务&#xff0c;租带宽&…

单链表逆序的多种方式

https://www.cnblogs.com/eniac12/p/4860642.htmltemplate<class T> void List<T>::Inverse() {if(first NULL) return;LinkNode<T> *p, *prev, *latter; p first->link;   // 当前结点prev NULL;   // 前一结点l…

Linux系统【四】进程间通信-管道

进程间通信&#xff08;IPC Interprocess Communication&#xff09; 进程和进程之间的通信只能通过内核&#xff0c;在内核中提供一块缓冲区进行通信。内核提供的这种机制叫做IPC 在进程间完成数据传输需要借助操作系统提供的特殊方法&#xff0c;如&#xff1a;文件&#xf…