Linux--生产者与消费者

http://blog.csdn.net/gebushuaidanhenhuai/article/details/74011636

基本概念

提到生产者和消费者,我们最有可能想到的是商店卖东西,顾客在货架上(缓冲区)买东西。 
生产者消费者问题,其实是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程—即所谓的“生产者”和“消费者”–在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放在缓冲区中,消费者在缓冲区消耗这些数据。但是,要保证生产者不会在缓冲区满时还往缓冲区写数据,消费者也不会在缓冲区为空时读数据。 
生产者与消费者

三种关系


  • 生产者与消费者之间是供求关系(互斥和同步)
  • 生产者与生产者之间是竞争关系(互斥)
  • 消费者与消费者之间是竞争关系(互斥)

我们简单解释一下三种关系。假如我们现在在一家超市,我们们想要买一箱牛奶。牛奶生产商(生产者)生产了牛奶,经超市工作人员把牛奶摆放在了货架上,在这个过程过我们(消费者)不能买牛奶,要等待工作人员摆好货物,所以此时生产者与消费者是互斥关系。工作人员摆好货物后,我们(消费者)去购买,此时生产者与消费者是同步关系。 
一个货架上只能摆一个品牌的货物,怒能摆其他的,此时生产者与生产者之间是互斥关系。 
两个或多个顾客不能同时买一个货物,此时消费者与消费者之间是互斥关系。

我们可以用两种方法实现生产者与消费者模型。

基于单链表的生产者消费者模型

我们用两个线程分别表示生产者与消费者,用单链表表示缓冲区。 
生产者生产数据,插入到单链表的头部。 
消费者消费数据,从单链表的头部读数据。

条件变量


条件变量是利用线程间共享的全局变量进行同步的一种机制,只要包括两个动作:一个线程等待”条件变量的条件成立”而挂起;另一个线程使”条件成立(给出条件成立信号)。 
为了放置竞争,条件变量的使用总和一个互斥锁结合在一起。

条件变量的类型为 pthread_cond_t. 
条件变量的初始化:

1.直接定义一个全局的条件变量,并利用宏PTHREAD_COND_INITIALIZER进行值得初始化。

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  • 1

2.调用函数pthread_cond_init

#include<pthread.h>
pthread_cond_init (pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
  • 1
  • 2
  • 3

第一个参数即为我们动态分配的条件变量cond,除非创建一个非默认属性的条件变量,否则第二个参数attr始终为NULL; 
注意:若不想讲条件变量定义成全局的,必须以动态分配的方式创建。

pthread_cond_t *cond = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));
  • 1
  • 2
  • 3

注意:使用此种方式,先destroy条件变量,再free这块空间。

3.销毁条件变量

int pthread_cond_destroy(pthread_cond_t* cond);
  • 1

参数cond指针即指向我们创建的条件变量。 
4.等待 
我们使用pthread_cond_wait或pthread_cond_timewait函数等待条件变量变为真。

int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
  • 1
  • 2

pthread _cond_wait,其第一个参数是指向条件变量的指针,第二个参数是一个指向互斥锁的指针。在上面提到过,条件变量总是和一把互斥锁协同使用的。目的是为了防止资源的竞争。 
生产者与消费者之间是同步互斥关系的,他们不能同时访问缓冲区,所以我们需要一把锁来约束他们。 
假如我们此时有两个消费者A,B在等待资源,生产者申请到了”锁“,并且生产了一个产品,释放锁。并发送信号告诉消费者你们可以来消费了。 
假如消费者A 率先抢到锁,买走了产品。B再申请到锁时,发现已经没有产品了,只能等待条件变量为真时,买产品。此时锁在B身上,如果B一直在等待,一直不释放锁时,会造成生产者申请不到锁而造成“死锁”。所以wait的第二个参数就是当消费者在申请到锁时,条件变量为假时,及时的释放锁资源。 
wait函数是无条件等待。在条件变量为假时,会一直等下去。timedwait是有条件等待,它多定义了一个超时,超时值定义了我们愿意等待多长时间。它通过timespec决定。

5.发送信号

int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
  • 1
  • 2

当生产者生产完毕后需要通知消费者。发送信号有两种方式。signal是根据某种优先级唤醒一个等待者。broadcast是在资源充足的情况下进行广播,唤醒所有等待者。

代码实现:


#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>typedef struct _list
{struct _list *next;int _val;
}product_list;product_list *head = NULL;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t need_product = PTHREAD_COND_INITIALIZER;void Init_list(product_list* list)
{if(list != NULL){list -> next = NULL;list -> _val = 0;}
}void* Consumer(void* _val)
{product_list *p = NULL;for(;;){pthread_mutex_lock(&lock);while(head == NULL){pthread_cond_wait(&need_product,&lock);}p = head;head = head -> next;p -> next = NULL;pthread_mutex_unlock(&lock);printf("Consum success,val is:%d\n",p -> _val);free(p);}return NULL;
}void* Product(void* _val)
{for(;;){sleep(rand() % 2);product_list* p =malloc(sizeof(product_list));pthread_mutex_lock(&lock);Init_list(p);p -> _val = rand() % 1000;p -> next = head;head = p;pthread_mutex_unlock(&lock);printf("Call consumer! Product has producted,val is:%d\n",p->_val);pthread_cond_signal(&need_product);}
}int main()
{pthread_t t_product;pthread_t t_consumer;pthread_create(&t_product,NULL,Product,NULL);pthread_create(&t_consumer,NULL,Consumer,NULL);pthread_join(t_product,NULL);pthread_join(t_consumer,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
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

这里写图片描述

基于环形队列的生产者消费者模型

除了基于单链表的生产者与消费者模型,我们还可以利用信号量实现生产者消费者模型。

原理

生产者在空格子上生产数据。 
消费者在有商品的格子上消费数据。 
注意:

  • 生产者先进行生产。
  • 当消费者没有数据要消费时,需等待生产者生产。
  • 当生产者把缓冲区充满时,需等待消费者消费,出现空格子时在生产。 
    这里写图片描述

操作函数

#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);
int sem_trywait(sem_t *sem);
int sem_post(sem_t *sem);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

初始化信号量sem_init,参数value为信号量的值,参数pshared一般设为0,表示信号量用于同一进程内线程间同步。摧毁信号量sem_destroy。P操作(申请资源)sem_wait,使信号量的值-1。V操作(释放资源)sem_post,使信号量的值+1。sem_trywait是尝试申请资源。

代码实现

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>#define _SIZE_ 5
sem_t blanks; //表示格子的信号量
sem_t datas;  //表示商品的信号量
int buf[_SIZE_] ={ 0 };//生产者
void* product(void* arg)
{int i = 0;while(1){usleep(500000);sem_wait(&blanks); //生产者申请格子资源int data = rand()%1000;buf[i] = data;printf("Product is:%d\n",data);sem_post(&datas); //每生产一个商品就需要对商品信号量+1++i;i %= _SIZE_;}
}//消费者
void* consumer(void* arg)
{int i = 0;while(1){usleep(500000);   sem_wait(&datas);  //消费者申请商品资源printf("Consumer is%d\n",buf[i]);sem_post(&blanks); //买走一个商品,就多了一个空格子++i;i %= _SIZE_;}
}int main()
{sem_init(&blanks,0,_SIZE_);sem_init(&datas,0,0);pthread_t _consumer;pthread_t _product;pthread_create(&_consumer,NULL,consumer,NULL);pthread_create(&_product,NULL,product,NULL);pthread_join(_consumer,NULL);pthread_join(_product,NULL);sem_destroy(&blanks);sem_destroy(&datas);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
  • 60
  • 61

这里写图片描述


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

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

相关文章

进程的挂起以及可重入函数

相关接口     pause 函数用于将进程挂起. 如果信号的处理动作是终止进程, 则进程终止, pause 函数没有返回值; 如果信号的处理动作是忽略, 则进程被挂起, pause函数不返回, 如果信号的处理动作是捕捉, 则调用信号处理动作之后pause 返回 -1.来看一段代码 #include<s…

POJ1236Network of Schools——强连通分量缩点建图

【题目描述】 A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B is in the distri…

C——通过调用函数分配内存

http://blog.csdn.net/u012627502/article/details/3579724 1&#xff09;以返回值方式返回&#xff1a;把动态分配的存储位置地址&#xff0c;赋值给指针类型返回值&#xff08;不同于被调用函数的自动变量地址&#xff09; 2&#xff09;以形参形式返回&#xff1a;二级指针类…

gdb调试多进程程序

1.gdb下调试多进程程序只需要以下几条命令即可              除此之外还可以查看正在调试的进程 info inferiors, 同时也可以将当前正在调试的进程切换到另外一个进程中让其取运行     2.代码调试演示 #include<stdio.h> #include<stdlib.h> #…

BZOJ1123-BLO——强连通分量求割点+计数

【题目描述】 Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 所有towns连通。Input 输入n<100000 m<500000及m条边Output 输出n个数&#xff0c;代表如果把第i个点去掉&#xff0c;将有多少对点不能互通。Sample Input 5…

关于memcpy和memmove两函数的区别

http://blog.csdn.net/caowei840701/article/details/8491836 [cpp] view plaincopy <p> 关于memcpy和memmove两个c标准库函数&#xff0c;其功能都是将一块内存区域中的指定大小内容复制到目标内存中&#xff0c;在翻阅c标准库实现的源代码我们发现他们是有区别的。&…

判断字符串出栈合法性

先来看说一下思路 接下来就是写代码了 int StackOrder(SeqStack* stack, char* input, char* output, int size_input, int size_output) {if(stack NULL || input NULL || output NULL){return 0;}int i_input 0;int j_output 0;SeqStackType value;for(; j_output <…

CodeForces - 1200C——小模拟

【题目描述】 Amugae is in a very large round corridor. The corridor consists of two areas. The inner area is equally divided by n sectors, and the outer area is equally divided by m sectors. A wall exists between each pair of sectors of same area (inner o…

1 单例模式

达内 闵大神 //饿汉单例模式 #include <iostream> using namespace std;class Singleton { public:static Singleton& getInstance(){return s_instance;} private:Singleton(){}Singleton(const Singleton& that){}static Singleton s_instance;//静态成员变量 …

共享栈

1.定义 所谓共享栈就是利用一个数组实现两个栈. 先来看一下共享栈的数据结构 typedef struct SharedStack {int top1;int top2;SeqStackType* data; }SharedStack; 2. 初始化 void SharedStackInit(SharedStack* stack) {if(stack NULL){return;//非法输入}stack -> top…

BZOJ1018 | SHOI2008-堵塞的交通traffic——线段树维护区间连通性+细节

【题目描述】 BZOJ1018 | SHOI2008-堵塞的交通traffic 有一天&#xff0c;由于某种穿越现象作用&#xff0c;你来到了传说中的小人国。小人国的布局非常奇特&#xff0c;整个国家的交通系统可 以被看成是一个2行C列的矩形网格&#xff0c;网格上的每个点代表一个城市&#xff0…

C++ 函数隐藏

C该函数隐藏 只有基类成员函数的定义已声明virtualkeyword&#xff0c;当在派生类中的时间&#xff0c;以支付功能实现&#xff0c;virtualkeyword可以从时间被添加以增加。它不影响多状态。 easy混淆视听&#xff0c;掩盖&#xff1a; &#xff0c;规则例如以下&#xff1a; …

迷宫求解(递归)

首先来看一下迷宫简易图                                  我们用 0 来表示该位置是墙, 用 1 来表示该位置是路. 所以, 我们在处理迷宫问题的时候可以将其看成一个二维数组即可, 而对应的每一条路我们可以用坐标的形式将其表示, 所以还需要…

CodeForces - 786C——二分+模拟?

【题目描述】 Rick and Morty want to find MR. PBH and they cant do it alone. So they need of Mr. Meeseeks. They Have generated n Mr. Meeseeks, standing in a line numbered from 1 to n. Each of them has his own color. i-th Mr. Meeseeks color is ai.Rick and M…

迷宫求解(非递归)

上篇文章写出了利用函数形成栈桢的特性完成迷宫求解问题, 本篇文章我们自己手动维护一个栈, 其进行出栈, 入栈, 取栈顶元素, 来完成迷宫求解寻路的过程     思路和以前一样, 首先, 我们先定义一个栈, 对其初始化, 同时, 定义一个迷宫地图, 对该地图进行初始化, 先判断当前…

数据结构练习——双向链表

http://www.cnblogs.com/-Lei/archive/2012/04/10/2440399.html 复习一下数据结构。。。。说不准下个星期就用上了 只不过写的很简单&#xff0c;没有封装 DoubleLinkList.h #ifndef GUARD_DoubleLinkList_h #define GUARD_DoubleLinkList_h#include <stdio.h>struct Li…

CodeChef - DGCD——树链剖分+差分

【题目描述】 Youre given a tree on N vertices. Each vertex has a positive integer written on it, number on the ith vertex being vi. Your program must process two types of queries :1. Find query represented by F u v : Find out gcd of all numbers on the uniq…

UVa272-TeX中的引号

【题目描述】 传送门 【题目分析】 今天开始刷紫书的题目啦 这道题很简单&#xff0c;需要注意的是cgetchar()需要加上括号&#xff0c;因为赋值语句的优先级比判等低 而且书中说好像最好用整型变量&#xff0c;因为EOF的值为-1&#xff0c;在字符变量中没有这个值。&#xf…

C++中友元(友元函数和友元类)的用法和功能

http://blog.csdn.net/adriano119/article/details/5914443/ 采用类的机制后实现了数据的隐藏与封装&#xff0c;类的数据成员一般定义为私有成员&#xff0c;成员函数一般定义为公有的&#xff0c;依此提供类与外界间的通信接口。但是&#xff0c;有时需要定义一些函数&#x…

线程的终止分离

1.线程的终止 注意该函数是针对用户级别的, 其中 retal 必须指向一个全局变量, 或者是一个 malloc 分配的, 因为如果是线程的局部变量, 当该线程退出时, 其他线程不能得到这个变量, 因为线程的局部变量各自私有 2. 现成的取消 其中thread是线程的 tid 3.线程的等待与分离 (1)…