【并发编程十七】c++实现一个线程池

news2024/11/15 20:01:09

【并发编程十七】c++实现一个线程池

  • 一、线程池原理
  • 二、实现重点
  • 三、个人理解
  • 四、实验

  • 简介:
    大多数系统上,若因某些任务可以与其他任务并行处理,就分别给他们配备专属的线程,则这种做法不切实际。但是只要有可能,我们还是想充分利用可调配的并发算力。线程池正好可以帮助我们达到目的:讲可同时执行的任务都提交到线程池,再将其放入任务队列中等待;随后,队列中的任务分别由某一工作线程领取并执行,执行完成后,改线程再从任务队列中取出另外一任务来执行,如此循环往复。

一、线程池原理

  • 线程池其实只是一组线程。
  • 在一般情况下,我们需要异步执行一些任务,这些任务的产生和执行是存在于程序的整个生命周期的。
  • 与其让操作系统频繁低为我们创建和销毁线程,不如创建一组在程序生命周期内不会退出的线程。
  • 为了不浪费系统的资源,我们的基本要求是当有任务需要执行时,这些线程可以自动拿到任务并执行,在没有任务时这些线程处于阻塞或者睡眠状态。

二、实现重点

  • 既然在程序生命周期内会产生很多任务,那么这些任务必须有一个存放的地方,这个地方就是队列,所以不要一提到队列就认为它是1个具体的list,队列也可以是一个全局变量或链表。
  • 这在本质尚就是生产者、消费者模式,产生任务的线程是生产者,线程池中的线程是消费者。
  • 既然会有多个线程同时操作这个队列,那么根据多线程程序的原则,我们一般需要对这个队列线程加锁。
  • 在技术上除了要解决线程池创建、向任务队列中投递任务、从任务队列中取任务并处理的问题,我们还需要做一些善后的工作:线程池的清理、退出线程池中的工作线程、清零任务队列等。

三、个人理解

以下文字可以在理解完毕线程池的代码后再来看下

  • 我们可以把一个函数(或者类的方法)绑定给一个单独的线程执行。
  • 当然,我们也可以把一个函数绑定给多个线程,每个线程中执行的函数都相同。(数据不同)
  • 执行函数相同,而数据不同?绑定任务时,需要传入this,this就是给线程中执行的不同的数据。(因为需要操作不同的数据,所以需要加锁)
  • 只不过执行的过程会判断下任务队列中是否由任务,有任务才会执行。
    在这里插入图片描述

四、实验

  • demo
#include <iostream>
#include <atomic>
#include<thread>
#include<condition_variable>
#include<list>
#include<vector>
#include<memory>
#include<functional> // bind头文件

using namespace std;

//线程池要执行的具体的业务
class Task
{
public:
	virtual void doWork()
	{
		cout << "===================== start a work =====================\n";
	}
	virtual ~Task()
	{
		cout << "===================== finsh a work =====================\n";
	}
};

// 线程池的实现
class threadPool
{
public:
	threadPool();
	~threadPool();
	threadPool(const threadPool& rhs) = delete;
	threadPool& operator = (const threadPool& rhs) = delete;
public:
	void init(int threadNumber = 4);
	void stop();
	void addTask(Task* task);
	void clear();

	//void popTask(task* task);

private:
	void threadFunction();                     //线程池中要多个线程执行的函数(休眠函数)
private:
	vector<shared_ptr<thread>> m_threadVector; //线程池中的存线程的vector
	std::list<shared_ptr<Task>> m_taskList;    //任务队列
	mutex m_mutex;                             //因为多个线程(函数),同时操作任务队列,所以需要加函数
	condition_variable m_cv;                  //条件变量
	bool m_bRunging;                          //线程池状态

};

threadPool::threadPool() :m_bRunging(false){}

threadPool::~threadPool()
{
	clear();
}

void threadPool::init(int threadNum/*=5*/)
{
	if (threadNum <= 0)
		threadNum = 2;
	else if (threadNum > thread::hardware_concurrency())
		threadNum = thread::hardware_concurrency();

	m_bRunging = true;
	for (int i = 0; i < threadNum; i++)
	{
		shared_ptr<thread> spThread;
		//m_threadVector.push_back(thread(&threadPool::threadFunction, this));//如果m_threadVector是vector<thread>,就可以这样绑定。
		spThread.reset(new thread(std::bind(&threadPool::threadFunction, this)));
		m_threadVector.push_back(spThread);
		cout << "add a thread" << endl;
	}
}

void threadPool::threadFunction()
{
	shared_ptr<Task> spTask;
	while (true)
	{
		{
			unique_lock<mutex> guard(m_mutex);
			while (m_taskList.empty())
			{
				if (!m_bRunging)
					break;
				m_cv.wait(guard);
			}

			if (!m_bRunging)
				break;
			spTask = m_taskList.front();
			m_taskList.pop_front();
		}
		/*if (spTask == NULL)
			continue;*/

		if (spTask)
		{
			spTask->doWork();
			spTask.reset();
			cout << "exit thread and threadID=" << this_thread::get_id() << endl;
		}
		else
		{
			cout << "spTask is null\n";
		}
		
	}
	
}

void threadPool::stop()
{
	m_bRunging = false;
	m_cv.notify_all();

	for (auto& iter : m_threadVector)
	{
		if (iter->joinable())
			iter->join();
	}
}

void threadPool::addTask(Task* task)
{
	shared_ptr<Task> spTask;
	spTask.reset(task);

	{
		lock_guard<mutex> guard(mutex);
		m_taskList.push_back(spTask);
		cout << "add a task.\n";
	}
	
	m_cv.notify_one();
}

void threadPool::clear()
{
	lock_guard<mutex> guard(m_mutex);
	for (auto& iter : m_taskList)
		iter.reset();
	m_taskList.clear();
}

int main()
{
	threadPool m_threadPool;
	m_threadPool.init(55);

	Task* task = NULL;
	for (int i = 0; i < 10; i++)
	{
		task = new Task();
		m_threadPool.addTask(task);
	}

	this_thread::sleep_for(chrono::milliseconds(1000));

	m_threadPool.stop();

}
  • 输出
    在这里插入图片描述

参考:
1、《c++并发编程实战(第二版)》安东尼.威廉姆斯 著;吴天明 译;
2、《c++服务器开发精髓》 张远龙 著;

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

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

相关文章

C语言进阶——动态内存管理(上)

&#x1f307;个人主页&#xff1a;_麦麦_ &#x1f4da;今日名言&#xff1a;“你若爱&#xff0c;生活哪里都可爱。你若恨&#xff0c;生活哪里都可恨。你若感恩&#xff0c;处处可感恩。你若成长&#xff0c;事事可成长。不是世界选择了你&#xff0c;是你选择了这个世界。既…

mdio协议

1. 简介 MDIO接口中有特定的术语定义总线上的各种设备&#xff0c;驱动MDIO总线的设备被定义为站管理实体&#xff08;STA&#xff09;&#xff0c;而被MDC管理的目标设备称为可被MDIO管理的设备&#xff08;MMD&#xff09;。 STA初始化MDIO所有的通信&#xff0c;同时负责驱动…

【数据结构与算法】哈希表1:字母异位词 两数交集 快乐数 两数之和

文章目录今日任务1.哈希表理论基础&#xff08;1&#xff09;哈希表&#xff08;2&#xff09;哈希函数&#xff08;3&#xff09;哈希碰撞&#xff08;4&#xff09;链地址法&#xff08;拉链法&#xff09;&#xff08;5&#xff09;线性探测法&#xff08;6&#xff09;常见…

Python采集双色球数据,做数据分析,让我自己实现自己的富豪梦

来唠点嗑&#xff1f; 咳咳&#xff0c;最近是咋的了&#xff0c;某站掀起了一股双色球热潮&#xff1f;一般我自己的账号上&#xff0c;是很少看到关于python这些内容的&#xff0c;都是小姐姐和热梗&#xff0c;或者其他搞笑视频 由于&#x1f4b4;的吸引力…手不自觉的就点…

《系统架构设计》-03-软件结构体系和架构风格

文章目录1. 软件结构体系1.1 抽象&#xff08;Abstract&#xff09;1.1.1 抽象的应用1.1.2 不同层次的抽象1.2 组件&#xff08;Component&#xff09;1.2.1 定义1.2.2 切入点1.3 组织过程资产&#xff08;Organizational Process Assets&#xff09;1.3.1 定义1.3.2 作用1.4 体…

springboot整合Chat Generative Pre-trained Transformer

什么是Chat Generative Pre-trained Transformer Chat Generative Pre-trained Transformer&#xff0c;是以人工智能驱动的聊天机器人程序 &#xff0c;已经更新多个版本&#xff0c;很多大厂也都在接入其API。 整合难度 难度一颗星&#xff0c;基本上就是给官方API发请求&am…

特征工程:特征构造以及时间序列特征构造

数据和特征决定了机器学习的上限&#xff0c;而模型和算法只是逼近这个上限而已。由此可见&#xff0c;特征工程在机器学习中占有相当重要的地位。在实际应用当中&#xff0c;可以说特征工程是机器学习成功的关键。 那特征工程是什么&#xff1f; 特征工程是利用数据领域的相关…

UI自动化测试之设计框架

目的 相信做过测试的同学都听说过自动化测试&#xff0c;而UI自动化无论何时对测试来说都是比较吸引人的存在。 相较于接口自动化来说它可以最大程度的模拟真实用户的日常操作与特定业务场景的模拟&#xff0c;那么存在即合理&#xff0c;自动化UI测试自然也是广大测试同学职…

身为大学生,你不会还不知道有这些学生福利吧!!!!

本文介绍的是利用学生身份可以享受到的相关学生优惠权益&#xff0c;但也希望各位享受权利的同时不要忘记自己的义务&#xff0c;不要售卖、转手自己的学生优惠资格&#xff0c;使得其他同学无法受益。 前言 高考已经过去&#xff0c;我们也将迎来不同于以往的大学生活&#x…

磁盘结构

一.盘片 盘片是一个圆形坚硬的表面&#xff0c;通过引入磁性变化来永久存储数据&#xff0c;这些盘片通常由一些硬质材料&#xff08;如铝&#xff09;制成&#xff0c;然后涂上薄薄的磁性层&#xff0c;即使驱动器断电&#xff0c;驱动器也能持久存储数据位。每个盘片有两面&a…

袋鼠云高教行业数字化转型方案,推进数字化技术和学校教育教学深度融合

在当前的数字化转型浪潮下&#xff0c;“基础设施、配套设备、应用探索”的数字校园1.0阶段即将步入尾声、亦或已经完结&#xff0c;不同地区和类型的高校通过各类信息化系统和基础设施已经初步实现了业务数字化&#xff0c;整个数字校园的信息基础设施底座已有一定基础、信息时…

TCP编程之网卡信息获取和域名解析

TCP编程之网卡信息获取和域名解析 1.TCP/IP简介 TCP/IP协议源于1969年&#xff0c;是针对Internet开发的一种体系结构和协议标准&#xff0c;目的在于解决异种计算机网络的通信问题。使得网络在互联时能为用户提供一种通用、一致的通信服务。是Internet采用的协议标准。   …

三菱PLC的MC协议配置说明

三菱PLC的MC协议配置说明先说一下弱智的踩坑记录详细配置过程1、三菱Q02H CPUQJ71E71-100以太网模块设置MC协议1.1 PLC编程线连接与编程线驱动安装1.2 PLC通讯测试1.3 PLC MC协议设置1.4 PLC断点重启1.5 网络调试助手测试2、三菱Q03UDE CPU内置以太网设置MC协议2.1 PLC编程线连…

决策树算法和CART决策树算法详细介绍及其原理详解

相关文章 K近邻算法和KD树详细介绍及其原理详解朴素贝叶斯算法和拉普拉斯平滑详细介绍及其原理详解决策树算法和CART决策树算法详细介绍及其原理详解 文章目录相关文章前言一、决策树算法二、CART决策树算法2.1 基尼系数2.2 CART决策树算法总结前言 今天给大家带来的主要内容包…

虹科分享 | 网络流量监控 | 你的数据能告诉你什么:解读网络可见性的4种数据类型

要了解网络性能问题的原因&#xff0c;可见性是关键。而这四种数据类型&#xff08;流、数据包、SNMP和API&#xff09;都在增强网络可见性方面发挥着重要作用。 流 流是通过网络发送的数据的摘要。流类型不同&#xff0c;可以包括NetFlow, sFlow, jFlow和IPFIX。不同的流类型…

SPF动物实验室设计,SPF动物实验室装修SICOLAB

SPF&#xff08;特殊病原体自由&#xff09;动物实验室规划设计SICOLAB&#xff08;一&#xff09;设计原则为了建造一座SPF&#xff08;特殊病原体自由&#xff09;动物实验室&#xff0c;需要采取以下步骤&#xff1a;&#xff08;1&#xff09;选址&#xff1a;选择远离其他…

lombok注解@Data使用在继承类上时出现警告解决方案

lombok为我们提供了Data注解&#xff0c;帮助我们省略了Setter,Getter,ToString等注解&#xff0c;一般对于普通的实体类使用该注解&#xff0c;不会出现什么问题&#xff0c;但是当我们把这个注解&#xff0c;使用在派生类上&#xff0c;就出现了一个警告1 情景再现父类:Data …

SESAM 安装教程

SESAM &#xff08;Super Element Structure Analysis Module&#xff09;是由挪威船级社&#xff08;DNV-GL&#xff09;开发的一款有限元分析&#xff08;FEA&#xff09;系统&#xff0c;它以 GeniE、HydroD 和 DeepC 等模块为核心&#xff0c;主要用于海工结构的强度评估、…

leaflet 绘制两个多边形的交集、差集、并集(083)

第083个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中如何获取两个多边形的交集、差集、并集。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共148行)安装插件相关API参考:专栏目标示例效果 配…

Python日期时间模块

Python 提供了 日期和时间模块用来处理日期和时间&#xff0c;还可以用于格式化日期和时间等常见功能。 时间间隔是以秒为单位的浮点小数。每个时间戳都以自从 1970 年 1 月 1 日午夜&#xff08;历元&#xff09;经过了多长时间来表示。 一、time模块使用 Time 模块包含了大…