C++11多线程编程——线程池的实现

news2024/9/27 9:30:21

学一门新技术,还是要问那个问题,为什么我们需要这个技术,这个技术能解决什么痛点。

一、为何需要线程池

那么为什么我们需要线程池技术呢?多线程编程用的好好的,干嘛还要引入线程池这个东西呢?引入一个新的技术肯定不是为了装酷,肯定是为了解决某个问题的,而服务端一般都是效率问题。

我们可以看到多线程提高了CPU的使用率和程序的工作效率,但是如果有大量的线程,就会影响性能,因为要大量的创建与销毁,因为CPU需要在它们之间切换。线程池可以想象成一个池子,它的作用就是让每一个线程结束后,并不会销毁,而是放回到线程池中成为空闲状态,等待下一个对象来使用。

 

二、C++中的线程池

但是让人遗憾的是,C++并没有在语言级别上支持线程池技术,总感觉C++委员会对多线程的支持像是犹抱琵琶半遮面的羞羞女一样,无法完全的放开。

虽然无法从语言级别上支持,但是我们可以利用条件变量和互斥锁自己实现一个线程池。这里就不得不啰嗦几句,条件变量和互斥锁就像两把利剑,几乎可以实现多线程技术中的大部分问题,不管是生产消费者模型,还是线程池,亦或是信号量,所以我们必须好好掌握好这两个工具。

 

#ifndef _THREADPOOL_H
#define _THREADPOOL_H
#include <vector>
#include <queue>
#include <thread>
#include <iostream>
#include <condition_variable>
using namespace std;
 
const int MAX_THREADS = 1000; //最大线程数目
 
template <typename T>
class threadPool
{
public:
    threadPool(int number = 1);
    ~threadPool();
    bool append(T *task);
    //工作线程需要运行的函数,不断的从任务队列中取出并执行
    static void *worker(void *arg);
    void run();
 
private:
    //工作线程
    vector<thread> workThread;
    //任务队列
    queue<T *> taskQueue;
    mutex mt;
    condition_variable condition;
    bool stop;
};
 
template <typename T>
threadPool<T>::threadPool(int number) : stop(false)
{
    if (number <= 0 || number > MAX_THREADS)
        throw exception();
    for (int i = 0; i < number; i++)
    {
        cout << "create thread:" << i << endl;
        workThread.emplace_back(worker, this);
    }
}
template <typename T>
inline threadPool<T>::~threadPool()
{
    {
        unique_lock<mutex> unique(mt);
        stop = true;
    }
    condition.notify_all();
    for (auto &wt : workThread)
        wt.join();
}
template <typename T>
bool threadPool<T>::append(T *task)
{
    //往任务队列添加任务的时候,要加锁,因为这是线程池,肯定有很多线程
    unique_lock<mutex> unique(mt);
    taskQueue.push(task);
    unique.unlock();
    //任务添加完之后,通知阻塞线程过来消费任务,有点像生产消费者模型
    condition.notify_one();
    return true;
}
template <typename T>
void *threadPool<T>::worker(void *arg)
{
    threadPool *pool = (threadPool *)arg;
    pool->run();
    return pool;
}
template <typename T>
void threadPool<T>::run()
{
    while (!stop)
    {
        unique_lock<mutex> unique(this->mt);
        //如果任务队列为空,就停下来等待唤醒,等待另一个线程发来的唤醒请求
        while (this->taskQueue.empty())
            this->condition.wait(unique);      
        T *task = this->taskQueue.front();
        this->taskQueue.pop();
        if (task)
            task->process();
    }
}
#endif

 

三、线程池代码解析

  1. 对于线程池ThreadPool,必须要有构造和析构函数,构造函数中,创建N个线程(这个自己指定),插入到工作线程当中,工作线程可以是vector结构。工作线程中的线程具体要做什么呢?进入线程的时候必要用unique_lock进程加锁处理,不能让其他线程以及主线程影响到要处理的这个线程。判断任务队列是否为空,如果为空,则利用条件变量中的wait函数来阻塞该线程,等待任务队列不为空之后唤醒它。然后取出任务队列中的任务,执行任务中的具体操作。
  2. 接着将任务放入任务队列taskQueue,这里的任务是外部根据自己的业务自己定义的,可以是对象,可以是函数,结构体等等,而任务队列这里定义为queue结构,一定要记得将任务放入任务队列的时候,要在之前加锁,放入之后再解锁,这里的加锁解锁可以用unique_lock结构,当然也可以用mutex结构,而放入任务队列之后就可以用条件变量的notify_one函数通知阻塞的线程来取任务处理了。
  3. 看过我之前写的《生产消费者模型之条件变量》的朋友对以上代码有点熟悉,没错,线程池的实现就有点像是生产消费者模型,append()就像是生产者,不断的将任务放入队列,run()函数就像消费者,不断的从任务队列中取出任务来处理,生产消费的两头分别用notify_one()和wait()来唤醒和阻塞。更加详细的介绍可以去看我的上一篇文章。
  4. 最后写一个main文件来调用线程池的相关接口,main文件里定义一个任务对象,然后是main函数。
#include "threadPool.h"
#include <string>
using namespace std;
class Task
{
private:
    int total = 0;
 
public:
    void process();
};
 
//任务具体实现什么功能,由这个函数实现
void Task::process()
{
    //这里就输出一个字符串
    cout << "task successful! " << endl;
    this_thread::sleep_for(chrono::seconds(1));
}
 
template class std::queue<Task>;
int main(void)
{
    threadPool<Task> pool(1);
    std::string str;
    while (1)
    {
        Task *task = new Task();
        pool.append(task);
        delete task;
    }
}

 以上就是线程池的实现部分,充分利用条件变量和互斥锁来实现,模型可以参考生产消费者模型。以上代码部分来自网络,根据自己的需求更改一些。

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

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

相关文章

发展文旅夜游项目有哪些好处

夜晚的城市&#xff0c;总是充满着无限的魅力和活力&#xff0c;而文旅夜游更是让这份魅力和活力得到了更好的展现和发挥。新起典文旅科技认为文旅夜游不仅仅是一种旅游方式&#xff0c;更是可以增加城市夜间经济、丰富文化娱乐生活、缓解白天拥堵、提高旅游体验、促进文化交流…

HTTP的特点

灵活可扩展 HTTP 协议最初诞生的时候就比较简单&#xff0c;本着开放的精神只规定了报文的基本格式&#xff0c;比如用空格分隔单词&#xff0c;用换行分隔字段&#xff0c;“headerbody”等&#xff0c;报文里的各个组成部分都没有做严格的语法语义限制&#xff0c;可以由开发…

大厂面试NLP工程师,会考察你哪些方面的能力?

你好&#xff0c;我是周磊。 相信你已经知道&#xff0c;一名AI算法工程师&#xff0c;不但需要基础能力扎实&#xff0c;更要具备良好的工程落地能力。那在NLP工程师面试的时候&#xff0c;你知道面试官会从哪些维度去考察你这两方面的能力吗&#xff1f; 今天我就结合我的一…

一种用于大坝水库边坡内部振弦式应变计组

1用途 多向应变计组适用于长期埋设在水工结构物或其它混凝土结构物内&#xff0c;测量结构物内部各个方向上的应变量&#xff0c;并可同步测量埋设点的温度。 应变计按方向和支数安装在应变计安装支座上&#xff0c;组成多向应变计组&#xff0c;用于测量大体积混凝土中各方向…

SpringCloud------热部署(三)

SpringCloud------热部署&#xff08;三&#xff09; Devtools是热部署插件&#xff0c;引入热部署实现高效自测。 步骤&#xff1a; 1.Adding devtools to your project 2.Adding plugin to your project 3.Enabling automatic build 4.Update the value of 点击 ctrlshiftal…

大型互联网企业大流量高并发电商领域核心项目已上线(完整流程+项目白皮书)

说在前面的话 面对近年来网络的飞速发展&#xff0c;大家已经都习惯了网络购物&#xff0c;从而出现了一些衍生品例如&#xff1a;某宝/某东/拼夕夕等大型网站以及购物APP~ 并且从而导致很多大型互联网企业以及中小厂都需要有完整的项目经验&#xff0c;以及优秀处理超大流量…

Mysql数据库迁移|如何把一台服务器的mysql数据库迁移到另一台服务器上的myql中

前言 那么这里博主先安利一下一些干货满满的专栏啦&#xff01; Linux专栏https://blog.csdn.net/yu_cblog/category_11786077.html?spm1001.2014.3001.5482操作系统专栏https://blog.csdn.net/yu_cblog/category_12165502.html?spm1001.2014.3001.5482手撕数据结构https:/…

为什么要进行数据决策?数据决策对企业而言有何重要意义?

“大数据”几乎已成为时下最时髦的词汇&#xff0c;不夸张地说&#xff0c;当今各行各业无不对大数据充满了向往&#xff0c;希望自己在新一轮的大数据营销中抢占先机。同时&#xff0c;从大数据中引申出的数据挖掘、数据分析、数据安全等数据运用技术也成为人们热捧的焦点。 …

PMP课堂模拟题目及解析(第2期)

11. 涉及一家全球公司 10 家分公司的项目即将完成&#xff0c;项目经理正在安排召开一次经验教训总结会议&#xff0c;项目经理应该邀请谁来参加会议&#xff1f; A. 所有干系人 B. 项目经理的业务部门主管 C. 项目团队的所有成员 D. 高级管理层 12. 收集完有关项目的…

C++标准库 --- 动态内存 (Primer C++ 第五版 · 阅读笔记)

C标准库 --动态内存 (Primer C 第五版 阅读笔记&#xff09; 第12章 动态内存------(持续更新)12.1、动态内存与智能指针12.1.1、shared_ptr类12.1.2、直接管理内存12.1.3、shared_ptr和new结合使用12.1.4、智能指针和异常12.1.5、unique_ptr12.1.6、weak_ptr 12.2、动态数组1…

刷题记录2

文章目录 题目一分析题解 题目二分析题解 题目一 牛牛定义排序子序列为一个数组中一段连续的子序列,并且这段子序列是非递增或者非递减排序的。牛牛有一个长度为n的整数数组A,他现在有一个任务是把数组A分为若干段排序子序列,牛牛想知道他最少可以把这个数组分为几段排序子序列…

小林coding——图解系统

文章目录 硬件结构CPU是如何执行程序的&#xff1f;磁盘比内存慢几万倍&#xff1f;如何写出让CPU跑得更快的代码&#xff1f;CPU缓存一致性 操作系统结构Linux内核 VS Winodws内核 内存管理为什么要有虚拟内存&#xff1f;malloc是如何分配内存的&#xff1f;内存满了&#xf…

Python 中 随机数 random库 学习与使用

python中的随机数应用大体包含&#xff0c;随机整数、随机浮点数 和 获取随机数序列三类。 一. 随机整数 1.1 随机整数&#xff0c;包含上下限&#xff08;闭区间&#xff09; randint(a, b)&#xff1a;随机选取 [a, b] 之间的一个整数&#xff0c;随机整数包含a 和 b&#…

mybatis配置参数

一、基础配置 1. 连接数据库 spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/cscardata?useUnicodetrue&characterEncodingUTF-8&serverTimezoneGMT%2B8username: rootpassword: roottype: com.alibaba.druid.poo…

公开景区监控——缓解交通拥堵,优化出行体验

阚昊宇 XXXXXXXXXXXXX 1 引言 随着疫情时代过去&#xff0c;旅游需求不断增长&#xff0c;节假日热点景区的人流量持续攀高。在节假日调休机制的共同影响下&#xff0c;旅游景区内人群密度不断增加。5月3日&#xff0c;携程发布的《2023年五一出游数据报告》显示&#xff0c;…

15天学习MySQL计划-主从复制(运维篇)第十三天

15天学习MySQL计划-主从复制&#xff08;运维篇&#xff09;第十三天 主从复制 1.概述 ​ 主从复制是指主数据库的DDL和DML操作通过二进制日志传到从库服务器中&#xff0c;然后在从库上对这些日志重新执行&#xff08;也叫做重做&#xff09;&#xff0c;从而使得从库和主库…

计算机操作系统实验:银行家算法模拟

目录 前言实验目的实验内容实验原理实验过程代码如下代码详解算法过程运行结果 总结 前言 本文是计算机操作系统实验的一部分&#xff0c;主要介绍了银行家算法的原理和实现。银行家算法是一种用于解决多个进程对多种资源的竞争和分配的算法&#xff0c;它可以避免死锁和资源浪…

写作业用白光还是暖光?盘点色温4000K的护眼台灯

台灯的白光或者暖光指的是台灯的色温&#xff0c;低色温的光线看起来发黄发红&#xff0c;高色温的光线发白发蓝。 如果灯光的光源是高品质光源&#xff0c;本身没有蓝光问题&#xff0c;那么色温的选择对护眼的影响是比较少的&#xff0c;更多的是对人学习工作状态&#xff0c…

TIM-编码器—STM32

TIM-编码器—STM32 EncoderInterface 编码器接口 编码器接口可接收增量(正交) 编码器的信号&#xff0c;根据编码器旋转产生的正交信号脉冲&#xff0c;自动控制CNT自增或自减&#xff0c;从而指示编码器的位置、旋转方向和旋转速度 每个高级定时器和通用定时器都拥有1个编码器…

transformer在计算机视觉中的应用

Transformer 在计算机视觉中的应用 论文地址&#xff1a; https://arxiv.org/abs/1706.03762 Vision Transformer An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale 代码地址&#xff1a; https://github.com/google-research/vision_transfor…