C++线程池(1)理论基础及简单实现

C++—线程池(附源代码) std::atomic 提供了原子操作,适用于需要在多线程环境中进行无锁操作的场景。std::atomic_flag 用于实现低级别的锁机制,如自旋锁。了解内存序是使用原子操作时的一个重要方面,它帮助你控制操作的顺序和可见性,从而确保程序的正确性和效率。 阅读详情

写过CURD的程序员肯定都熟悉“数据库连接池”这个东西,数据库连接是个昂贵的东西,因为它的创建过程比较耗费资源。所以为了节约资源、提高数据库操作的性能,“数据库连接池”就应运而生了。

其实线程池跟数据库连接池类似,一个线程的创建和销毁也需要耗费资源,如果反复地创建和删除线程,那么资源的浪费也是很可观的。所以,预先创建一些线程,在有任务的时候分配给相应的线程去执行,对提高系统的性能有非常积极的作用。

一、线程池的创建过程

1、创建线程

for (size_t i = 0; i < threadCount; ++i) {
    threads.emplace_back(threadFunc, this);
}

threads是个vector,我们可以先创建一些线程,并把这些线程放到一个vector中。

2、任务队列与调度

void ThreadPool::addTask(const Task& task) {
    {
        lock_guard<mutex> lock(queueMutex);
        taskQueue.emplace(task);
    }
    condition.notify_one();
}

新进来的任务会被加到taskQueue队列中,你可以根据一定的策略从taskQueue中取出任务,交给某个空闲的线程去执行。

3、线程执行及回收

void ThreadPool::threadFunc() {
    while (true) {
        Task task;
        {
            unique_lock<mutex> lock(queueMutex);
            //wait()用来等一个东西
            //1. 如果第2个参数为false,就跟只有1个参数一样,直接阻塞到本行
            //阻塞到什么时候为止呢?有其他持有锁的线程notify()后.
            //2. 当wait()被notify()唤醒后,会先判断第2个参数是否为true,如果为true才会
            //继续往下执行.
            condition.wait(lock, [this]() { return !taskQueue.empty() || terminate; });

            if (terminate && taskQueue.empty()) {
                break;
            }

            task = taskQueue.front();
            taskQueue.pop();
        }
        task(); // Execute the task.
    }
}

4、线程池的优雅终止

在多线程程序中,如何终止线程是一个需要“慎重”考虑的问题,你不能直接给stop了,因为这会导致某个正在运行的线程被硬生生地中断,会导致内部数据的不一致!

我们需要先添加一个标志:terminate,当terminate标记为true时,先把线程池的while任务给停了,然后唤醒所有等待中的线程,使用std::thread::join()函数等待线程执行完毕。

ThreadPool::~ThreadPool() {
    terminate = true;
    condition.notify_all(); // 唤醒所有等待中的线程

    for (thread& th : threads) {
        if (th.joinable()) {
            th.join(); // 等待线程执行完毕
        }
    }
}

二、一个线程池的例子


#include <vector>
#include <queue>
#include <thread>
#include <iostream>
#include <stdexcept>
#include <condition_variable>
#include <memory>

#define MAX_THREADS 8 //最大线程数目

class Task{
public:
    void process(){
	
	}
};

class ThreadPool {
public:
    ThreadPool() = default;
	
    ThreadPool(const ThreadPool &) = delete;
    ThreadPool(ThreadPool &&) = delete;
    ThreadPool &operator = (const ThreadPool &) = delete;
    ThreadPool &operator = (ThreadPool &&) = delete;

	//初始化所有线程并启动线程
	void init(int threadCount){
		if (threadCount <= 0 || threadCount > MAX_THREADS) {
			throw std::exception();
		}

		for (int i = 0; i < threadCount; i++) {
			// emplace_back不能先构造再插入. 
			// 方法2: std::thread temp(worker, this); work_threads.push(temp);
			work_threads.emplace_back(worker, this);
		}
	}

	bool dispatch(std::shared_ptr<Task>&& request){
		// 操作工作队列时一定要加锁, 因为他被所有线程共享
		std::unique_lock<std::mutex> lock(queue_mutex);
		lock.lock();
		tasks_queue.push(request);
		lock.unlock();

		// 线程池添加进去了任务, 通知等待的线程
		condition.notify_one();

		return true;
	}

	~ThreadPool(){
		std::unique_lock<std::mutex> lock(queue_mutex);
		stop = true;
		 
		condition.notify_all();
		for (auto& w : work_threads) {
			w.join();
		}
	}

private:
	// 工作线程需要运行的函数,不断的从任务队列中取出并执行
	static void* worker(void* arg){
		ThreadPool* pool = (ThreadPool*)arg;
		pool->run();

		return pool;
	}

	void run(){
		while (!stop)
		{
			// unique_lock() 出作用域会自动解锁
			std::unique_lock<std::mutex> lk(this->queue_mutex);

			// 如果任务队列不为空,就停下来等待唤醒
			this->condition.wait(lk, [this] {
				return !this->tasks_queue.empty(); 
			});

			std::shared_ptr<Task> request = tasks_queue.front();
			tasks_queue.pop();
			if (request) {
				request->process();
			}
		}
	}

private:
	// 工作线程
	std::vector<std::thread> work_threads;
	
	// 任务队列
	std::queue<std::shared_ptr<Task>> tasks_queue;

	std::mutex queue_mutex;
	std::condition_variable condition;
	
	bool stop = false;
};

int main(){
    ThreadPool pool;
	pool.init(4);
	
    return 0;
}

参考:

(1)基于C++11实现线程池

(2)轻松掌握C++线程池:从底层原理到高级应用

Linux - C++实现简单线程池 Linux下,用C++实现简单线程池。线程安全的单例模式 阅读详情

相关推荐

C++ threads.emplace_back不同参数用法

在C++中,使用类创建线程是一个常见需求。类提供了多种方式来构造线程对象,包括直接传递可调用对象(如函数、lambda表达式、成员函数指针等)以及它们的参数。方法通常与一起使用,以便在容器中直接构造线程对象,而不是先创建再赋值。

专注基础架构领域 759

C++多线程

线程创建 1.C++11的标准库中的多线程库 #include<thread> 2.创建线程的格式 : 关键字thread 线程名(入口函数,入口函数的参数); 若入口函数参数比较多,按照顺序往后写,用英文逗号隔开。 3.子线程开始运行的两张方式: join() 子线程阻塞主线程,子线程运行完之后,主线程再次开始运行; detach() 主线程和子线程同时运行,如果主线程需要使用子线程的数据,在用到子线程数据的地方,子线程还没有运行结束,那么这个时候主线程使用的数据还是子线程最初的数

sang_12345的博客 2165

C++新特性实现高效线程池,程序性能瞬间提升!

本文深入探讨了如何利用C++新特性实现高效线程池,提升程序性能。首先介绍了C++11和C++14中提供的新特性,如std::thread、std::future、std::function等,并解释了它们如何用于线程池的实现。然后详细介绍了线程池的设计原理和实现过程,包括任务队列的管理、线程的管理以及线程池的调度策略。通过充分利用C++新特性,使得线程池在高并发场景下表现出色,大大提升了程序的性能和响应速度。

Lion_Long的博客 4247

C++线程池

C++线程池

weixin_45144862的博客 2万+

Linux下分别使用C和C++语言实现线程池(附带源码和解析)

本文记录了分别使用C和C++语言实现线程池的过程,并附带源码和解析。 一、线程池理论知识 1.1什么是线程池 线程池(thread pool): 一种线程使用模式,线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在短时间任务创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数据取决于可用的并发处理器、处理器内核、内存、网络sockets等数量。 因为程序边运行边创建线程是比较耗时的,所以我们通过池化的

AnChenliang_1002的博客 711

C++实现的线程池Reactor模式HTTP服务器

网络编程是构建分布式系统和网络应用的基石。了解网络协议的层次结构,能让我们更好地理解C++网络编程中的关键概念,例如套接字编程、TCP/IP协议栈、以及网络字节序和主机字节序之间的转换规则。一些流行的网络编程库包括ASIO、Boost.Asio、Poco等。ASIO是一个现代C++库,专门用于网络和低级I/O编程,广泛用于异步网络编程领域。Boost.Asio则是Boost库中的网络和I/O组件,提供了跨平台的异步编程接口。线程池是管理一组同构工作线程的执行系统组件。

weixin_35756637的博客 1013

C++实现线程池

采用C++新特征实现线程池,用到含有自定义Any类型来接收任意数据类型,实现多线程并发运行

qq_48930954的博客 2万+

C++线程池实现详解与代码分析

一个线程池主要包括以下组件:任务队列:存储待处理的任务。工作线程:从任务队列中获取任务并执行。同步机制:确保线程安全,协调工作线程之间的工作。调度策略:任务分配和线程管理的方法。启动与关闭机制:线程池的初始化和优雅关闭。异常处理与性能优化:确保线程池稳定运行和性能调优。接下来的章节将会详细介绍这些组件的实现原理与技术细节。在C++中,线程的创建可以通过类实现,它是C++11标准库中的一个组件,用于表示和管理线程。

weixin_42124497的博客 422

C++线程池介绍和C++代码实现

C++线程池的介绍和使用场景,C++线程池demo实现。

hanxiaoyong_的博客 2098

【路由交换】思科5506防火墙三区域互通

思科 5506 inside outside dmz 互通

baidu_22774701的博客 6206

MQTT与物联网平台(一):基础知识和设备接入物联网平台的实现(以华为云为例)

本文简单介绍了MQTT的基础知识和使用方法,以蓝牙WIFI双模芯片W801作为硬件设备,接入到华为云。

weixin_50622833的博客 1万+

基于Linux/C++简单线程池的实现

我们知道Java语言对于多线程的支持十分丰富,JDK本身提供了很多性能优良的库,包括ThreadPoolExecutor和ScheduleThreadPoolExecutor等。C++11中的STL也提供了std:thread(然而我还没有看,这里先占个坑)还有很多第三方库的实现。这里我重复“造轮子”的目的还是为了深入理解C++和Linux线程基础概念,主要以学习的目的。 首先,为什么要使用线程池。因为线程的创建、和清理都是需要耗费系统资源的。我们知道Linux中线程实际上是由轻量级进程实现的,相对于纯理

木牛的博客 262

熵编码(五)-CABAC(一) 基础篇

前两篇算术编码文章介绍了算术编码原理和步骤。本篇文章介绍CABAC,基于上下文自适应的二进制算术编码。本文对CABAC的基本原理进行了详细的介绍和分析,下篇文章将会对H264编码中的CABAC熵编码的实际应用进行介绍。

尹佑壮的博客 3883

C++11实现线程池(1)概念

在处理大量并发任务时,传统方式(来一个任务创建一个线程)会因大量线程的创建、销毁消耗过多系统资源,还增加线程上下文切换开销。线程池技术预先创建一定数量线程,任务请求到来时从池中分配线程处理任务,线程处理完任务后归还到池中,继续为后续任务服务。线程池特点线程复用:内部维护一定数量线程,重复使用执行任务,避免频繁创建和销毁线程,提升性能与效率。控制并发性:针对多核处理器,合理分配线程到多个处理器,通过设定线程池大小限制同时执行的线程数量,避免资源过度占用和线程竞争导致性能下降。任务排队。

IKUN2333的博客 1276

【QT】基于QSerialPort的跨平台串口通信实战指南

本文是一份基于Qt QSerialPort模块的跨平台串口通信实战指南。详细介绍了从硬件基础、QSerialPort核心类使用、参数配置、设备自动发现到数据异步读写和错误处理的完整流程,并通过构建一个功能齐全的串口调试助手实例,帮助开发者快速掌握在Windows、Linux和macOS系统下进行高效、稳定的串口应用开发。

a1b2c3d的博客 982

【赛题解析&命令脚本配置】2023年全国职业院校技能大赛GZ073网络系统管理赛项赛题第10套模块A:网络构建 & (五)网络运维配置+(六)SDN网络配置-命令配置

【赛题解析&命令脚本配置】2023年全国职业院校技能大赛GZ073网络系统管理赛项赛题第10套模块A:网络构建 & (五)网络运维配置+(六)SDN网络配置-命令配置

君逍遥o 810

cpp11实现线程池(一)——项目介绍

线程池是库的形式提供给用户,是必须放到代码中,不能单独运行,亦称为基础组件第一版线程池任务对象使用继承技术,提供一个抽象基类Task,里面有一个纯虚函数run(),使用时继承该类,并重写该纯虚函数,在这个重写的纯虚函数中完成要执行的具体任务;第二版线程池使用C++11 的来包装任务执行函数,用来接受任意类型的结果。

qq_42120843的博客 1101

汽车标定技术(五)--基于模型开发如何生成完整的A2L文件(1)

A2L文件的生成分为两章讲解:本文主要是讲模型开发,讲解如何把标定量、观测量放到链接文件中的指定位置。下一篇讲解如何使用matlab生成完整的A2L文件

认真搞搞汽车MCU 4619

C++17手写线程池

实现通用C++线程池框架

immediatly的博客 1840

PCL 提取两片点云的非重叠部分【2025最新版】

PCL 提取两片点云的非重叠部分。博客长期更新,本文最近一次更新时间为:2025年9月1日。

点云侠的博客 36万+

BPC坏点矫正

whatis the Bad Pixel? 坏点为死点,也就是基本不随照度变化呈现光电线性转换的关系。表现为暗态常亮,亮态常暗。 坏点分类:静态坏点:亮坏点,暗坏点。 动态坏点:在一定像素范围内,该点表现正常,而超过这一范围,该点表现的比周围像素要亮。与sensor ...

camerall 1万+

线程池的简单实现

在学习使用stl容器的时候,我们见到了stl容器使用了内存池来进行时间优化,因为用户在使用new、malloc等向系统申请空间的时候,身份会发生变化,同时有可能要执行操作系统内的内存处理算法,这可能会比较耗时。于是使用了内存池来提前申请好大块内存空间,需要用户自行进行管理。与内存池类似,提前准备好的线程,用来随时处理任务,这就被称作线程池。 线程池是线程的一种使用模式。线程过多会带来调度开销,从而影响缓存局部性和整体性能。而线程池中维护着多个线程,等待分配可并发执行的任务。这就避免了在处理短时间任务时创建和

h1091068389的博客 530

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Linux——简单线程池封装案例

本文介绍了Linux环境下C++线程池技术的实现方法。首先阐述了线程池的基本概念、适用场景及优势,包括资源复用、流量控制和统一管理等核心作用。随后详细讲解了Linux线程基础,重点分析了POSIX线程库的常用函数和线程同步机制。文章还深入探讨了线程池实现的关键组件,包括任务队列设计、线程管理和同步机制。最后,通过条件变量封装的代码示例,展示了线程池核心实现步骤,为开发高性能并发程序提供了实用指导。

qq_34434522的博客 924

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Linux:线程池和单例模式

线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价(用空间换时间的一种策略)。

weixin_51142926的博客 8410

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

【Linux】 实现一个简单的线程池

线程池的概念 “池”的概念大家应该不陌生,线程池顾名思义就是存放线程的“池子”。 它是一种线程的使用模式。我们发现如果线程过多会带来调度的开销,进而影响缓存的局部性和整体性能。 而线程池维护着一定数量的线程,等待监督管理者分配可并发执行的任务,从而避免了在处理短时间任务时创建和销毁线程的代价。所以线程池不仅能够保证内核的充分利用,还能防止线程的过分调度。 总结:线程池很好的解决了在处理任务时的效...

ly_6699的博客 324
上一篇: std::bind的讲解
下一篇: QT学习笔记:QT的信号与槽
老蔡编程
博客等级 码龄23年 48粉丝 75原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值