生产者消费者问题是计算机中一类重要的模型,主要描述的是:生产者往缓冲区中放入产品、消费者取走产品。生产者和消费者指的可以是线程也可以是进程。

生产者消费者问题的难点在于:

为了缓冲区数据的安全性,一次只允许一个线程进入缓冲区,它就是所谓的临界资源。

生产者往缓冲区放物品时,如果缓冲区已满,那么需要等待,一直到消费者取走产品为止。

消费者取走产品时,如果没有物品,需要等待,一直到有生产者放入为止。

第一个问题属于互斥问题,我们需要使用一把互斥锁,来实现对缓冲区的安全访问。

后两个属于同步问题,两类线程相互协作,需要两个条件变量,一个用于通知生产者放入产品,一个用来通知消费者取走物品。

生产者线程的大概流程是:

1.加锁

2.如果缓冲区已满,则等待。

3.放入物品

4.解锁

5.通知消费者,可以取走产品

消费者的逻辑恰好相反:

1.加锁

2.缓冲区为空,等待

3.取走物品

4.解锁

5.通知生产者,可以放入物品

我们设计出一个缓冲区:

#ifndef BUFFER_H_
#define BUFFER_H_ #include "NonCopyable.h"
#include "MutexLock.h"
#include "Condition.h"
#include <queue> class Buffer : NonCopyable
{
public:
Buffer(size_t size);
void push(int val);
int pop(); bool empty() const;
size_t size() const; private:
mutable MutexLock mutex_;
Condition full_;
Condition empty_; size_t size_; //缓冲区的大小
std::queue<int> queue_;
}; #endif //BUFFER_H_

这里注意,我们把同步与互斥的操作都放入Buffer中,使得生产者和消费者线程不必考虑其中细节,这符号软件设计的“高内聚、低耦合”原则。

还有一点,mutex被声明为mutable类型,意味着mutex在const函数中仍然可以被改变,这是符合程序的逻辑的。把mutex声明为mutable,是一种标准实践。

重点是push和pop的实现:

void Buffer::push(int val)
{
//lock
//wait
//push
//notify
//lock
{
MutexLockGuard lock(mutex_);
while(queue_.size() >= size_)
empty_.wait();
queue_.push(val);
}
full_.notify();
} int Buffer::pop()
{
int temp = 0;
{
MutexLockGuard lock(mutex_);
while(queue_.empty())
full_.wait();
temp = queue_.front();
queue_.pop();
}
empty_.notify(); return temp;
}

这里注意:

1.条件变量的等待必须使用while,这是一种最佳实践,原因可见Condition的封装Linux组件封装(二)中条件变量Condition的封装

2.可以先notify再解锁,也可以先解锁。不过推荐先解锁,原因是如果先notify,唤醒一个线程B,但是还未解锁,此时如果线程切换至刚唤醒的线程B,B马上尝试lock,但是肯定失败,然后阻塞,这增加了一次线程切换的开销

我们还可以继续封装,将缓冲区与多个生产者、消费者封装成一个车间类,如下:

#ifndef WORKSHOP_H_
#define WORKSHOP_H_ #include "NonCopyable.h"
#include "Buffer.h"
#include <vector> class ProducerThread;
class ConsumerThread; class WorkShop : NonCopyable
{
public:
WorkShop(size_t bufferSize,
size_t producerSize,
size_t consumerSize);
~WorkShop(); void startWorking();
private:
size_t bufferSize_;
Buffer buffer_; size_t producerSize_;
size_t consumerSize_;
std::vector<ProducerThread*> producers_;
std::vector<ConsumerThread*> consumers_;
}; #endif //WORKSHOP_H_

这样就可以方便的指定线程的数目。

 

完整的项目代码请参见这里:生产者消费者完整代码

Linux组件封装(五)一个生产者消费者问题示例的更多相关文章

  1. 并发编程(二):分析Boost对 互斥量和条件变量的封装及实现生产者消费者问题

    请阅读上篇文章<并发编程实战: POSIX 使用互斥量和条件变量实现生产者/消费者问题>.当然不阅读亦不影响本篇文章的阅读. Boost的互斥量,条件变量做了很好的封装,因此比" ...

  2. 并发编程入门(二):分析Boost对 互斥量和条件变量的封装及实现生产者消费者问题

    请阅读上篇文章<并发编程实战: POSIX 使用互斥量和条件变量实现生产者/消费者问题>.当然不阅读亦不影响本篇文章的阅读. Boost的互斥量,条件变量做了很好的封装,因此比" ...

  3. Linux多线程之同步2 —— 生产者消费者模型

    思路 生产者和消费者(互斥与同步).资源用队列模拟(要上锁,一个时间只能有一个线程操作队列). m个生产者.拿到锁,且产品不满,才能生产.当产品满,则等待,等待消费者唤醒.当产品由空到不空,通知消费者 ...

  4. 五、生产者消费者模型_ThreadLocal

    1.生产者消费者模型作用和示例如下:1)通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率 ,这是生产者消费者模型最重要的作用2)解耦,这是生产者消费者模型附带的作用,解耦意味着生产者 ...

  5. 用阻塞队列实现一个生产者消费者模型?synchronized和lock有什么区别?

    多线程当中的阻塞队列 主要实现类有 ArrayBlockingQueue是一个基于数组结构的有界阻塞队列,此队列按FIFO原则对元素进行排序 LinkedBlockingQueue是一个基于链表结构的 ...

  6. 用Java写一个生产者-消费者队列

    生产者消费者的模型作用 通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要的作用. 解耦,这是生产者消费者模型附带的作用,解耦意味着生产者和消费者之间的联系 ...

  7. golang的生产者消费者模型示例

    package main import "fmt" func Producer(ch chan int) { for i := 1; i <= 10; i++ { ch &l ...

  8. Python 再次改进版通过队列实现一个生产者消费者模型

    import time from multiprocessing import Process,Queue #生产者 def producer(q): for i in range(10): time ...

  9. Linux组件封装(一)中互斥锁MutexLock的封装

    本文对Linux中的pthread_mutex_t做一个简易的封装. 互斥锁主要用于互斥,互斥是一种竞争关系,主要是某一个系统资源或一段代码,一次做多被一个线程访问. 条件变量主要用于同步,用于协调线 ...

随机推荐

  1. 将PHP程序中返回的JSON格式数据用gzip压缩输出

    //phpinfo(); 搜索下 zlib 是否开启 //此示例开启压缩 Content-Length:124 Size: 404B //未开启gzip压缩 Content-Length:675 Si ...

  2. 汕头市队赛 SRM 07 A 你的麻将会排序吗

    A 你的麻将会排序吗 SRM 07 曾经有过一些沉迷日麻的小孩纸,后来呀,他们都去寻找自己的世界了. kpm也是这样的小孩纸.他想有一只自动整理牌的机器.当麻将以给定的顺序进入机器时,通过机器的运转, ...

  3. 汕头市队赛 SRM 09 B 撕书

    B 撕书II-3 SRM 09 背景&&描述 琉璃手头有一黑一白两本魔法书,一本是<缟玛瑙的不在证明>,另一本是<白色相簿1.5>     传说同时打开这两本书 ...

  4. 洛谷 P2916 [USACO08NOV]为母牛欢呼Cheering up the C…

    题目描述 Farmer John has grown so lazy that he no longer wants to continue maintaining the cow paths tha ...

  5. 关于JSON的简介及取值以及常见面试题

    关于JSON的简介及取值 JSON(JavaScript Object Notation)一种轻量级的数据交互格式 类似于一种数据封装,可以想象为java中student封装类 JSON的数值可以是数 ...

  6. [ CodeVS冲杯之路 ] P1294

    不充钱,你怎么AC? 题目:http://codevs.cn/problem/1294/ 随手一打就是这么漂亮的全排列,想当年我初一还是初二的时候,调了1个多小时才写出来(蒟蒻一枚) 直接DFS每次枚 ...

  7. 配置Nginx来支持php

    安装php7     下载地址:https://secure.php.net/downloads.php这里下载的是:wget http://ar2.php.net/distributions/php ...

  8. List<?>和List<Class<?>>区别及泛型相关

    ?表示是任意类型,但是编译器不能确定他是什么类型,所以你add的时候什么参数也不能传给它Class<?>表示任意类型的Class对象,list里面可以添加任何类型的Class对象,其它的对 ...

  9. mysql常用命令行操作

    1.linux下彻底卸载mysql sudo rm /var/lib/mysql/ -Rsudo rm /etc/mysql/ -R sudo apt-get autoremove mysql* -- ...

  10. ES6新特性及用法笔记

    1.新增数据类型Symbol.[Number.Boolean.Symbol.Null.Undefined.Object] Symbol类型的值通过Symbol函数生成,相同的Symbol函数返回的值 ...