再谈多线程模型之生产者消费者(多生产者和多消费者 )(c++11实现)
0.关于
为缩短篇幅,本系列记录如下:
再谈多线程模型之生产者消费者(基础概念)(c++11实现)
再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现)
再谈多线程模型之生产者消费者(单一生产者和多消费者)(c++11实现)
再谈多线程模型之生产者消费者(多生产者和单一消费者 )(c++11实现)
再谈多线程模型之生产者消费者(多生产者和多消费者 )(c++11实现)【本文】
再谈多线程模型之生产者消费者(总结)(c++11实现)
本文涉及到的代码演示环境: VS2017
欢迎留言指正
1. 多生产者&多消费者
- 1.1 相对一对一和一对多,多对多则是一对一和多对多的结合体。
- 1.2 生产者有多个,且其相互之间存在竞争
- 1.3 消费者有多个,其其相互之间存在竞争
- 1.4 大家共用一个缓冲区,还要考虑生产者与消费者之间的T同步情况。
- 1.5 结构体模型是这样的:
template<typename T>
struct repo_
{
// 用作互斥访问缓冲区
std::mutex _mtx_queue;
// 缓冲区最大size
unsigned int _count_max_queue_10 = 10;
// 缓冲区
std::queue<T> _queue;
// 缓冲区没有满,通知生产者继续生产
std::condition_variable _cv_queue_not_full;
// 缓冲区不为空,通知消费者继续消费
std::condition_variable _cv_queue_not_empty;
// 用于生产者之间的竞争
std::mutex _mtx_pro;
// 计算当前已经生产了多少数据了
unsigned int _cnt_cur_pro = 0;
// 用于消费者之间的竞争
std::mutex _mtx_con;
// 计算当前已经消费多少数据了
unsigned int _cnt_cur_con = 0;
repo_(const unsigned int count_max_queue = 10) :_count_max_queue_10(count_max_queue)
, _cnt_cur_con(0)
{
;
}
repo_(const repo_&instance) = delete;
repo_& operator = (const repo_& instance) = delete;
repo_(const repo_&&instance) = delete;
repo_& operator = (const repo_&& instance) = delete;
};
可见,相对单一消费者和单一生产者模型,多了下面的代码,用于解决竞争的问题。
// 用于生产者之间的竞争
std::mutex _mtx_pro;
// 计算当前已经生产了多少数据了
unsigned int _cnt_cur_pro = 0;
// 用于消费者之间的竞争
std::mutex _mtx_con;
// 计算当前已经消费多少数据了
unsigned int _cnt_cur_con = 0;
- 1.6 生产者线程
template< typename T >
void thread_pro(const int thread_index, const int count_max_produce, repo<T>* param_repo)
{
if (nullptr == param_repo || NULL == param_repo)
return;
while (true)
{
bool is_running = true;
{
// 用于生产者之间竞争
std::unique_lock<std::mutex> lock(param_repo->_mtx_pro);
// 缓冲区没有满,继续生产
if (param_repo->_cnt_cur_pro < cnt_total_10)
{
thread_produce_item<T>(thread_index, *param_repo, param_repo->_cnt_cur_pro);
++param_repo->_cnt_cur_pro;
}
else
is_running = false;
}
std::this_thread::sleep_for(std::chrono::microseconds(16));
if (!is_running)
break;
}
}
- 1.7 消费者线程
template< typename T >
void thread_con(const int thread_index, repo<T>* param_repo)
{
if (nullptr == param_repo || NULL == param_repo)
return;
while (true)
{
bool is_running = true;
{
std::unique_lock<std::mutex> lock(param_repo->_mtx_con);
// 还没消费到指定的数目,继续消费
if (param_repo->_cnt_cur_con < cnt_total_10)
{
thread_consume_item<T>(thread_index, *param_repo);
++param_repo->_cnt_cur_con;
}
else
is_running = false;
}
std::this_thread::sleep_for(std::chrono::microseconds(16));
// 结束线程
if ((!is_running))
break;
}
}
1.8 完整源码
#pragma once
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>
std::mutex _mtx;
std::condition_variable _cv_not_full;
std::condition_variable _cv_not_empty;
const int max_queue_size_10 = 10;
enum
{
// 总生产数目
cnt_total_10 = 10,
};
template<typename T>
struct repo_
{
// 用作互斥访问缓冲区
std::mutex _mtx_queue;
// 缓冲区最大size
unsigned int _count_max_queue_10 = 10;
// 缓冲区
std::queue<T> _queue;
// 缓冲区没有满,通知生产者继续生产
std::condition_variable _cv_queue_not_full;
// 缓冲区不为空,通知消费者继续消费
std::condition_variable _cv_queue_not_empty;
// 用于生产者之间的竞争
std::mutex _mtx_pro;
// 计算当前已经生产了多少数据了
unsigned int _cnt_cur_pro = 0;
// 用于消费者之间的竞争
std::mutex _mtx_con;
// 计算当前已经消费多少数据了
unsigned int _cnt_cur_con = 0;
repo_(const unsigned int count_max_queue = 10) :_count_max_queue_10(count_max_queue)
, _cnt_cur_con(0)
{
;
}
repo_(const repo_&instance) = delete;
repo_& operator = (const repo_& instance) = delete;
repo_(const repo_&&instance) = delete;
repo_& operator = (const repo_&& instance) = delete;
};
template <typename T>
using repo = repo_<T>;
//----------------------------------------------------------------------------------------
// 生产者生产数据
template <typename T>
void thread_produce_item(const int &thread_index, repo<T>& param_repo, const T& repo_item)
{
std::unique_lock<std::mutex> lock(param_repo._mtx_queue);
// 1. 生产者只要发现缓冲区没有满, 就继续生产
param_repo._cv_queue_not_full.wait(lock, [&] { return param_repo._queue.size() < param_repo._count_max_queue_10; });
// 2. 将生产好的商品放入缓冲区
param_repo._queue.push(repo_item);
// log to console
std::cout << "生产者" << thread_index << "生产数据:" << repo_item << "\n";
// 3. 通知消费者可以消费了
//param_repo._cv_queue_not_empty.notify_one();
param_repo._cv_queue_not_empty.notify_one();
}
//----------------------------------------------------------------------------------------
// 消费者消费数据
template <typename T>
T thread_consume_item(const int thread_index, repo<T>& param_repo)
{
std::unique_lock<std::mutex> lock(param_repo._mtx_queue);
// 1. 消费者需要等待【缓冲区不为空】的信号
param_repo._cv_queue_not_empty.wait(lock, [&] {return !param_repo._queue.empty(); });
// 2. 拿出数据
T item;
item = param_repo._queue.front();
param_repo._queue.pop();
std::cout << "消费者" << thread_index << "从缓冲区中拿出一组数据:" << item << std::endl;
// 3. 通知生产者,继续生产
param_repo._cv_queue_not_full.notify_one();
return item;
}
//----------------------------------------------------------------------------------------
/**
* @ brief: 生产者线程
* @ thread_index - 线程标识,区分是哪一个线程
* @ count_max_produce - 最大生产次数
* @ param_repo - 缓冲区
* @ return - void
*/
template< typename T >
void thread_pro(const int thread_index, const int count_max_produce, repo<T>* param_repo)
{
if (nullptr == param_repo || NULL == param_repo)
return;
while (true)
{
bool is_running = true;
{
// 用于生产者之间竞争
std::unique_lock<std::mutex> lock(param_repo->_mtx_pro);
// 缓冲区没有满,继续生产
if (param_repo->_cnt_cur_pro < cnt_total_10)
{
thread_produce_item<T>(thread_index, *param_repo, param_repo->_cnt_cur_pro);
++param_repo->_cnt_cur_pro;
}
else
is_running = false;
}
std::this_thread::sleep_for(std::chrono::microseconds(16));
if (!is_running)
break;
}
}
/**
* @ brief: 消费者线程
* @ thread_index - 线程标识,区分线程
* @ param_repo - 缓冲区
* @ return - void
*/
template< typename T >
void thread_con(const int thread_index, repo<T>* param_repo)
{
if (nullptr == param_repo || NULL == param_repo)
return;
while (true)
{
bool is_running = true;
{
std::unique_lock<std::mutex> lock(param_repo->_mtx_con);
// 还没消费到指定的数目,继续消费
if (param_repo->_cnt_cur_con < cnt_total_10)
{
thread_consume_item<T>(thread_index, *param_repo);
++param_repo->_cnt_cur_con;
}
else
is_running = false;
}
std::this_thread::sleep_for(std::chrono::microseconds(16));
// 结束线程
if ((!is_running))
break;
}
}
// 入口函数
//----------------------------------------------------------------------------------------
int main(int argc, char *argv[], char *env[])
{
// 缓冲区
repo<int> repository;
// 线程池
std::vector<std::thread> vec_thread;
// 生产者
vec_thread.push_back(std::thread(thread_pro<int>, 1, cnt_total_10, &repository));
vec_thread.push_back(std::thread(thread_pro<int>, 2, cnt_total_10, &repository));
// 消费者
vec_thread.push_back(std::thread(thread_con<int>, 1, &repository));
vec_thread.push_back(std::thread(thread_con<int>, 2, &repository));
for (auto &item : vec_thread)
{
item.join();
}
return 0;
}
- 1.9 可能的结果

再谈多线程模型之生产者消费者(多生产者和多消费者 )(c++11实现)的更多相关文章
- 再谈多线程模型之生产者消费者(总结)(c++11实现)
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...
- 再谈多线程模型之生产者消费者(多生产者和单一消费者 )(c++11实现)
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...
- 再谈多线程模型之生产者消费者(单一生产者和多消费者 )(c++11实现)
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...
- 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现)
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现)[本文] 再谈多线程模型之生 ...
- 再谈多线程模型之生产者消费者(基础概念)(c++11实现)
0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现)[本文] 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生 ...
- Java 多线程基础(十二)生产者与消费者
Java 多线程基础(十二)生产者与消费者 一.生产者与消费者模型 生产者与消费者问题是个非常典型的多线程问题,涉及到的对象包括“生产者”.“消费者”.“仓库”和“产品”.他们之间的关系如下: ①.生 ...
- Java 多线程详解(四)------生产者和消费者
Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html Java 多线程详解(二)------如何创建进程和线程: ...
- [Java基础] java多线程关于消费者和生产者
多线程: 生产与消费 1.生产者Producer生产produce产品,并将产品放到库存inventory里:同时消费者Consumer从库存inventory里消费consume产品. 2.库存in ...
- Java多线程-同步:synchronized 和线程通信:生产者消费者模式
大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...
随机推荐
- 如何使用scp在Linux服务器的后台传输文件?
目录 一.上传 常规操作 建议 后台运行 二.下载 两台服务器间文件如何传输?对于小文件,可以先从Linux服务器传到window,再传到另一台服务器.对于大的文件,如测序数据.比对文件等.这样的方法 ...
- Neville 插值方法
简介 wikipedia: Neville's method 在数学上,Neville 算法是一种计算插值多项式方法,由数学家Eric Harold Neville提出.由给定的n+1个节点,存在一个 ...
- R包customLayout比例拼图
一个简单的需求: 拼接两个图,一行两列,但不要一样大,让主图占的比例大些(如2/3),另一个图小一些(如1/3) 如上,我想突出曼哈顿图. R相关的拼图函数及包: 基础函数如par(mar =c(3, ...
- 【R shiny】一些应用记录
目录 DT和downloadButton应用 downloadButton 中验证结果输出 添加进度条 如何确保仅在使用Shiny按下操作按钮时才触发操作 其他 DT和downloadButton应用 ...
- CMSIS-RTOS 信号量Semaphores
信号量Semaphores 和信号类似,信号量也是一种同步多个线程的方式,简单来讲,信号量就是装有一些令牌的容器.当一个线程在执行过程中,就可能遇到一个系统调用来获取信号量令牌,如果这个信号量包含多个 ...
- C语言中储存的大小端问题
一.大小端定义 研究变量的高低字节:从左往右看,字节序递增,也就是最右边是最低字节,最右边是最高字节.如 int i = 0x01020304, 01是高字节,04是低字节.如果是字符串如char a ...
- 日常Java 2021/11/3
java网络编程 网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来.java.net包中J2SE的APl包含有类和接口,它们提供低层次的通信细节.你可以直接使用这些类和接口, ...
- Qt最好用评价最高的是哪个版本?
来源: http://www.qtcn.org/bbs/read-htm-tid-89455.html /// Qt4: 4.8.7 4.X 系列终结版本 Qt5 : 5.6 LT ...
- jenkins之授权和权限管理
#:创建角色,给角色授权,然后创建用户,将用户加入到角色(前提先安装插件) #:先将之前的卸载掉 #:然后重启服务,在可选插件搜索Role #:装完重启服务 root@ubuntu:~# system ...
- sql优化的8种方式 (下)
五.条件列表值如果连续使用between替代in 六.无重复记录的结果集使用union all合并 MySQL数据库中使用union或union all运算符将一个或多个列数相同的查询结 ...