Linux组件封装(五)一个生产者消费者问题示例
生产者消费者问题是计算机中一类重要的模型,主要描述的是:生产者往缓冲区中放入产品、消费者取走产品。生产者和消费者指的可以是线程也可以是进程。
生产者消费者问题的难点在于:
为了缓冲区数据的安全性,一次只允许一个线程进入缓冲区,它就是所谓的临界资源。
生产者往缓冲区放物品时,如果缓冲区已满,那么需要等待,一直到消费者取走产品为止。
消费者取走产品时,如果没有物品,需要等待,一直到有生产者放入为止。
第一个问题属于互斥问题,我们需要使用一把互斥锁,来实现对缓冲区的安全访问。
后两个属于同步问题,两类线程相互协作,需要两个条件变量,一个用于通知生产者放入产品,一个用来通知消费者取走物品。
生产者线程的大概流程是:
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组件封装(五)一个生产者消费者问题示例的更多相关文章
- 并发编程(二):分析Boost对 互斥量和条件变量的封装及实现生产者消费者问题
请阅读上篇文章<并发编程实战: POSIX 使用互斥量和条件变量实现生产者/消费者问题>.当然不阅读亦不影响本篇文章的阅读. Boost的互斥量,条件变量做了很好的封装,因此比" ...
- 并发编程入门(二):分析Boost对 互斥量和条件变量的封装及实现生产者消费者问题
请阅读上篇文章<并发编程实战: POSIX 使用互斥量和条件变量实现生产者/消费者问题>.当然不阅读亦不影响本篇文章的阅读. Boost的互斥量,条件变量做了很好的封装,因此比" ...
- Linux多线程之同步2 —— 生产者消费者模型
思路 生产者和消费者(互斥与同步).资源用队列模拟(要上锁,一个时间只能有一个线程操作队列). m个生产者.拿到锁,且产品不满,才能生产.当产品满,则等待,等待消费者唤醒.当产品由空到不空,通知消费者 ...
- 五、生产者消费者模型_ThreadLocal
1.生产者消费者模型作用和示例如下:1)通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率 ,这是生产者消费者模型最重要的作用2)解耦,这是生产者消费者模型附带的作用,解耦意味着生产者 ...
- 用阻塞队列实现一个生产者消费者模型?synchronized和lock有什么区别?
多线程当中的阻塞队列 主要实现类有 ArrayBlockingQueue是一个基于数组结构的有界阻塞队列,此队列按FIFO原则对元素进行排序 LinkedBlockingQueue是一个基于链表结构的 ...
- 用Java写一个生产者-消费者队列
生产者消费者的模型作用 通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要的作用. 解耦,这是生产者消费者模型附带的作用,解耦意味着生产者和消费者之间的联系 ...
- golang的生产者消费者模型示例
package main import "fmt" func Producer(ch chan int) { for i := 1; i <= 10; i++ { ch &l ...
- Python 再次改进版通过队列实现一个生产者消费者模型
import time from multiprocessing import Process,Queue #生产者 def producer(q): for i in range(10): time ...
- Linux组件封装(一)中互斥锁MutexLock的封装
本文对Linux中的pthread_mutex_t做一个简易的封装. 互斥锁主要用于互斥,互斥是一种竞争关系,主要是某一个系统资源或一段代码,一次做多被一个线程访问. 条件变量主要用于同步,用于协调线 ...
随机推荐
- 水(NOIP模拟赛Round #10)
题目描述: 小Z有一个长度为的数列.他有次令人窒息的操作,每次操作可以使某个数字或.他当然是希望这些数字的乘积尽量小了.为了简化题目,你只需输出操作完成后的数列即可. ———————————————— ...
- MFC中CTime获取日期时间的方法
MFC中CTime类的功能非常强大,可以获取年.月.日.小时.分钟.秒.星期等等,最最重要的是可根据需要去格式化.下面是具体的使用方式: ① 定义一个CTime类对象 CTime time; ② 得到 ...
- shell 将字符串分割成数组
代码:test.sh #!/bin/bash a="one,two,three,four" #要将$a分割开,可以这样: OLD_IFS="$IFS" IFS= ...
- gitlab 搭建与迁移
#下载gitlabhttps://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el6/gitlab-ce-10.1.3-ce.0.el6.x86_64.rpm ...
- App接口加密解密方法
//加密 function encrypt($data) { $key = md5("safregr"); $str = base64_encode($data); $res = ...
- 常用PHP数组函数总结
1.array_values() 以索引数组的形式返回数组的中所有元素的值 array_keys() 以索引数组的形式返回数组的中所有元素的值 2.in_array() 检查数组中是否存在某值 ...
- [BZOJ2815][ZJOI2012]灾难 灭绝树+拓扑排序+lca
灾难 [问题描述] 阿米巴是小强的好朋友. 阿米巴和小强在草原上捉蚂蚱.小强突然想,如果蚂蚱被他们捉灭绝了,那 么吃蚂蚱的小鸟就会饿死,而捕食小鸟的猛禽也会跟着灭绝,从而引发一系列的 生态灾难. 学过 ...
- Redis设置记录
首先大前提是,这个redis使用的外网端口,需要在防火墙或者安全组中打开 正常在redis配置文件里有个bind,这个默认是127.0.0.1,如果不修改,就是内网可以访问. 这里有个点需要提一下,就 ...
- 2018 ICPC 徐州邀请赛 总结
Day 0 上午在高铁里面,很困但是睡不着…… 中午到矿大报道. 食堂饭菜不错,有西瓜,就是有点辣. 下午热身赛,D题队友想了个假算法……GG. 评测机摸底考试正常进行. 热身赛之后精疲力尽,赶到宾馆 ...
- 1424 零树 (树形DP)
1424 零树 题意 给出一棵树,每次可以选择一个包含节点 1 的连通块,将所有的节点的权值同时加 1 或减 1 ,问最少多少次操作使所有节点权值变为 0 . 分析 这种题意简单的题目好处就是能很快知 ...