[C++]使用c++20信号量以及Impl模式的线程池

C++必知必会:pimpl惯用法 在日常开发中,类的public方法提供了对外接口供第三方使用,每个函数的具体实现都在XXX.cpp里,对第三方不可见。对于Windows系统上提供给第三方的库,库作者一般需要提供.h、.lib、.dll 文件给使用者,对于Linux系统则需要提供.h、.a、或 .so。不管那种系统,提供像XXX.h这样的头文件给第三方使用时, .h文件中类的大量成员变量和私有函数都暴露了这个类的太多实现细节,很容易让使用者看出其实现原理。这个类中暴露了成员变量 使用pimpl惯用法之后 所有的关键成员变量都已经不存在了,取 阅读详情

使用C++20的信号量来替代条件变量,
使用 impl 模式来隐藏具体的实现,可减少编译时间以及隐藏具体的细节实现。

PS:

  1. 任务队列如果使用无锁队列的话效率应该会有所提高。
  2. Impl模式可以隐藏数据成员以及实现,但是由于C++编译时需要确定类型的大小,所以使用智能指针。

头文件(接口)

ThreadPool.h:

#pragma once

#include <cstddef>
#include <memory>
#include <functional>

class ThreadPool {
private:
    void _enqueue(std::function<void()> task);

public:
    ThreadPool(const std::size_t threadnum);
    ~ThreadPool();
    template <typename Func, typename...Args> 
	inline void enqueue(Func &&f, Args&&...args) noexcept {
        return _enqueue(std::bind(std::forward<Func>(f), std::forward<Args>(args)...));
	}
private:
    // 使用 impl 来隐藏具体的实现,可减少编译时间以及隐藏具体的细节实现
    class Impl; 
    std::unique_ptr<Impl> m_handle;
};

源文件(实现)

ThreadPool.cpp:

#include <cstddef>
#include <atomic>
#include <memory>
#include <semaphore>
#include <vector>
#include <thread>
#include <mutex>

#include "ThreadPool.h"

constexpr unsigned EACH_THREAD_NUMS { 512 };

class ThreadPool::Impl {
    class Tasks;
private:
    std::unique_ptr<Tasks> m_tasks;
    // 用于管理空闲槽位
    std::counting_semaphore<> m_free_slots;	
    // 用于管理可用任务
    std::counting_semaphore<> m_available_tasks{0};	
    // 工作线程
	std::vector<std::thread> m_workers;		
    // 是否停止					
	std::atomic<bool> m_stop{false};							
private:
    void work_func();
public:
    Impl(const unsigned thread_nums);
    ~Impl();
    void enqueue(std::function<void()> &&task);
};

class ThreadPool::Impl::Tasks {
private:
    const unsigned m_nums;
	std::vector<std::function<void()>> m_tasks;
    unsigned m_start{0};  
	unsigned m_end{0};   
	unsigned m_count{0};
	std::mutex m_lock;	
public:
    Tasks(const unsigned nums);
    ~Tasks() = default;
    bool push(std::function<void()> &&in);
    bool pop(std::function<void()> &out);
};

ThreadPool::ThreadPool(const unsigned threadnum)
    : m_handle(std::make_unique<Impl>(threadnum)) {
    ;
}

ThreadPool::~ThreadPool() {
    ;
}

void ThreadPool::_enqueue(std::function<void()> task) {
    return m_handle->enqueue(std::move(task));
}

void ThreadPool::Impl::work_func() {
    while (true) {
        std::function<void()> task;
        m_available_tasks.acquire(); // 等待有任务
        if (!m_tasks->pop(task)) {
            if (m_stop) {
                return;
            }
            continue;
        }
        m_free_slots.release();
        if (task) {
            task();
        }
    }
}

ThreadPool::Impl::Impl(const unsigned thread_nums)
    : m_tasks(std::make_unique<Tasks>(thread_nums * EACH_THREAD_NUMS)), m_free_slots(thread_nums * EACH_THREAD_NUMS) {
    for (unsigned i { 0 }; i < thread_nums; ++i) {
        m_workers.emplace_back(&ThreadPool::Impl::work_func, this);
    }
}

ThreadPool::Impl::~Impl() {
    m_stop = true;
    // +256是为了防止虚假唤醒
    for (unsigned i = 0; i < m_workers.size() + 256; ++i) {
        m_available_tasks.release();
    }
    for (std::thread &worker : m_workers) {
        if (worker.joinable()) {
            worker.join();
        }
    }
}

void ThreadPool::Impl::enqueue(std::function<void()> &&task) {
    if (m_stop) {
        return;
    }
    m_free_slots.acquire();
    if (!m_tasks->push(std::move(task))) {
        return;
    }
    m_available_tasks.release();
}

ThreadPool::Impl::Tasks::Tasks(const unsigned nums)
    : m_nums(nums) {
    m_tasks.resize(nums);
}

bool ThreadPool::Impl::Tasks::push(std::function<void()> &&in) {
    std::lock_guard<std::mutex> lock(m_lock);
    if (m_nums == m_count) {
        return false;
    }
    m_tasks[m_end] = std::move(in);
    m_end = (m_end + 1) % m_nums;
    ++m_count;
    return true;
}

bool ThreadPool::Impl::Tasks::pop(std::function<void()> &out) {
    std::lock_guard<std::mutex> lock(m_lock);
    if (0 == m_count) {
        return false;
    }
    out = std::move(m_tasks[m_start]);
    m_start = (m_start + 1) % m_nums;
    --m_count;
    return true;
}

测试案例

test.cpp:

#include <chrono>
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <thread>
#include <atomic>

#include "ThreadPool.h"

static std::atomic<std::uint64_t> s_count;

void test_threadfunc(const int idx) {
	const std::size_t count{ s_count++ };
	for (int i { 0 }; i < 10; ++i) {
		// std::printf("[%llu]: Hello\n", count);
	}
	// std::printf("[%llu]: Hello\n", count);
}

int main() {
	try {
		const size_t threadCount = 3000; // 线程数量
		do {
			ThreadPool pool(threadCount);
			// 提交一些任务
			for (int i { 0 }; i < 65536; ++i) {
				pool.enqueue(test_threadfunc, i);
			}

			std::this_thread::sleep_for(std::chrono::seconds(5));
		} while (0);
		std::cout << "count: " << s_count << std::endl;
	}
	catch (const std::exception &e) {
		std::cout << "Exception: " << e.what() << std::endl;
	}

	return 0;
}
C++(20):线程池的实现和使用 线程池作用 线程池能够减少创建的线程个数,线程池的出现着眼于减少线程本身带来的开销 线程池适合场景 (1)单位时间内处理任务频繁而且任务处理时间短 (2)对实时性要求较高。如果接受到任务后在创建线程,可能满足不了实时要求,因此必须采用线程池进行预创建。 实现代码 代码来自github上的一位大神,只用一个简单的头文件就实现了线程池:https://github.com/progsc... 阅读详情

相关推荐

Linux C/C++进阶实战:从核心原理到AI Infra与音视频项目

C++作为一门系统级编程语言,其核心价值在于对硬件资源的直接控制与极致的运行时性能。其原理建立在RAII资源管理、零开销抽象及对内存模型的精细控制之上,这使得它在需要高性能和低延迟的领域具有不可替代的技术价值。在工程实践中,这些特性被广泛应用于基础设施、高性能计算和实时系统等关键场景。特别是在当前热门的AI基础设施(AI Infra)和音视频处理领域,C++凭借其高效的并行计算能力和对硬件加速器的直接操控,成为构建训练框架、推理引擎、编解码库的核心技术栈。本文将以AI Infra和音视频开发为具体切入点,系

weixin_30527423的博客 623

C++ IMPL 模式解析(上)

C++ IMPL 模式解析(上)

myw31415926的专栏 1万+

C++、Go、Rust混合架构实战:构建高性能产业政策分析平台

在现代软件工程中,多语言混合架构是解决复杂系统挑战的有效策略,其核心原理在于根据任务特性选择最合适的编程语言,以实现性能、开发效率与系统稳定性的最佳平衡。这种架构的技术价值在于能够充分发挥不同语言的优势,例如利用C++进行高性能计算,使用Go构建高并发服务,以及借助Rust保证内存安全。在应用场景上,它尤其适用于数据处理、科学计算和大型分布式系统。本文以产业政策分析平台为例,详细阐述了如何将C++、Go和Rust三种语言有机结合,通过gRPC和FFI等技术实现跨语言通信,并分享了在文档解析、任务调度和性能优

weixin_34343308的博客 450

java中impl作用

在Java开发中,通常将后台分成几层,常见的是三层mvc:Model、View、Controller,模型视图控制层三层,而impl通常处于controller层的service下,用来存放接口的实现类,impl的全称为implement,表示实现的意思。impl用于实现接口,通俗点,看图片理解一下: ...

ITSM/ITIL/网络安全/IT运维 1万+

【信息科学与工程学】【数据科学】数据科学领域 第六篇 算法设计 01

算法设计

weixin_49199313的博客 1501

【信息科学与工程学】【产品体系】第二十七篇 存储系统系列一 01

存储系统是一个横跨单机引擎 → 分布式系统 → 硬件指令集 → 云上多地域的四层技术栈。下面按你给的格式,用一组相互关联的表把"各类特性组合"一次性铺开。版本号以各厂商当前 GA 版本为准,功能清单为生产级能力摘要。编号存储系统(厂商 / 内核 / 引擎 / 版本 / 功能清单)S01Ceph(Red Hat / 自研内核模块 / RADOS + BlueStore + RocksDB / Squid 19.x)/ 功能:RBD 块、RGW 对象(S3/Swift)、CephFS POSIX、副本+EC 混

weixin_49199313的博客 366

跨平台信号量封装:POSIX与Windows核心差异与实战方案

信号量是并发编程中控制多线程或进程访问共享资源的核心同步原语。其原理是通过一个计数器管理资源访问权限,实现线程间的协调与互斥。在技术价值上,信号量能有效解决生产者-消费者、资源池管理等经典并发问题,提升系统稳定性和资源利用率。应用场景广泛,从操作系统内核到数据库连接池、任务队列等高性能服务均有涉及。本文聚焦于跨平台开发中的信号量实践,深入解析POSIX信号量与Windows信号量在API设计、内核对象模型和性能特性上的核心差异,并提供一套经过实战检验的、可复用的C/C++跨平台封装方案,帮助开发者在Linu

weixin_34203832的博客 386

Threadpool:轻便,快速,适用于C ++ 20的线程池

riften::Thiefpool 面向C ++ 20的超快速,轻量级,可窃取工作的线程池。 建立在无锁并发 。 用法 # include " riften/thiefpool.hpp " // Create thread pool with 4 worker threads. riften::Thiefpool pool ( 4 ); // Enqueue and return future. auto result = pool.enqueue([]( int x) { return x; }, 42 ); // Get result from future. std::cout << result.get() << std::endl; 另外, riften::Thiefpool提供了分离的入队版本: // Enqueue and return nothing pool.enq

机器人跨本体继承:桥接层设计与Linux实时调度实践

在机器人系统开发中,硬件抽象与软件解耦是提升代码复用性和系统可维护性的核心设计原则。其基本原理是通过定义统一的抽象接口,将上层智能算法与底层硬件驱动分离,从而实现算法在不同机器人平台间的无缝迁移。这一模式的技术价值在于显著降低了因硬件异构性带来的开发与适配成本,提升了机器人技术的规模化应用效率。在工程实践中,桥接层(Bridge Layer)是实现这一解耦的关键架构模式,它通过面向对象编程的依赖倒置原则,为移动平台、传感器等硬件组件提供标准化访问接口。应用场景广泛覆盖从移动机器人导航到机械臂控制等领域。本文

weixin_34194087的博客 399

浏览器主页被360篡改?教你彻底解决并预防再次发生

通过本文介绍的快捷方式修复、注册表清理、启动项管理三步法,可解决99%的浏览器主页篡改问题。权限最小化原则:非管理员账户日常使用,关键系统目录设置写保护定期安全审计:每月检查启动项/计划任务/浏览器扩展备份还原机制:创建系统还原点,重要操作前做好快照安全意识培养:不安装来源不明的软件,谨慎处理软件安装向导中的附加选项浏览器安全是系统防护的重要环节,建议结合EDR(终端检测与响应)方案构建立体防护体系。对于普通用户,保持系统更新、使用官方渠道下载软件、定期清理系统垃圾,即可有效预防绝大多数安全威胁。

nntxthml的博客 1万+

从零到一:手把手教你用SAM-BA工具完成嵌入式系统烧录

本文详细介绍了如何使用SAM-BA工具完成嵌入式系统烧录的全过程,包括环境搭建、硬件连接、Bootloader烧录以及完整系统镜像的烧录步骤。针对常见问题提供了实用的排查指南,并分享了批量烧录和自定义脚本的高级技巧,帮助开发者高效完成嵌入式系统开发。

weixin_42523792的博客 382

147.【2024Java八股-全网最全-10w字】

eg: 学校、院校、经验、年龄、跳槽频率…基本包括16种常见使用场景: 、数据共享分布式、、全局 ID、计数器、限流、位统计、购物车、用户消息时间线 timeline、、抽奖、点赞、签到、打卡、商品标签、商品筛选、用户关注、推荐模型、排行榜.详细讲解16种使用场景: https://blog.youkuaiyun.com/zxl646801924/article/details/123202381用户使用id进行查询文章的时候,首先会查询redis数据库,如果在redis中命中数据,那么redis就向用户返回查询的结果

qq_69683957的博客 1736

Code Ocean 上传代码 手把手教程

对于一些学术期刊,录用前会让把代码上传到Code Ocean上,但上传过程可能会遇到一些问题,小编分享一下自己的上传经验。他们会进行验证,验证通过,将自动发布。点此运行即可,但是运行比较慢。

u014071947的博客 1万+

Win32与Pthreads线程模型深度对比:从设计哲学到跨平台实战

并发编程是现代软件开发的核心技术,它通过多线程机制使程序能够同时执行多个任务,从而提升计算资源利用率和系统响应能力。其基本原理在于操作系统对CPU时间片的调度与管理,允许不同线程在宏观上并行执行。从技术价值看,并发编程能显著优化I/O密集型与计算密集型应用的性能,广泛应用于服务器后端、图形处理、科学计算及实时系统等场景。在具体实现层面,开发者常需面对不同操作系统提供的原生线程API,其中Windows平台的Win32线程与遵循POSIX标准的Pthreads便是两大主流模型。本文聚焦于这两者的深度对比,剖析

weixin_30785593的博客 297

新手必看!Unity中Tag和Layer的7个实际应用场景(含MOBA案例)

本文深入探讨Unity中Tag和Layer的7个实际应用场景,特别以MOBA游戏开发为例,详细解析如何利用LayerMask进行高效场景管理和碰撞检测优化。从双相机方案到射线检测的精准控制,帮助开发者掌握这些基础功能的进阶用法,提升游戏性能和开发效率。

weixin_28723171的博客 313

新能源充电桩充电枪口标准

建立的标准确保了新能源汽车充电的安全性、兼容性和效率,促进了新能源汽车产业的发展。

网络安全的研究者、新能源行业的追求者 4689

MYSQL 网络连接

assert(mp!= nullptr);#endif首先断言mp非空。为结构体动态分配内存,并将指针赋给。调用来初始化这个安全互斥锁,同时传入源文件名和行号。内部通常会:再调用初始化真正的原生互斥体。记录锁的创建位置、线程 ID 等信息,以便在死锁检测、锁定顺序检查或错误报告时提供调试信息。这种模式主要用于调试版本,帮助开发者发现互斥锁使用问题(如重复锁定、死锁、未释放等)。#else#endif直接调用初始化中的原生互斥体(例如或 Windows 的。

Piemon&Anokata的博客 497

【论文阅读】LIME:Low-light Image Enhancement via Illumination Map Estimation(笔记最全篇)

文章目录AbstractIntroductionContributionMethodSpeed-up Method(1):ALM子问题 T子问题 G子问题 Z和μ\muμSpeed-up Method(2):权重变量实验conclusion 今天要整理的一篇论文是使用传统的方法解决低照度的问题,Guo等人于2016年发表在IEEE上,论文《LIME:Low-light Image Enhance...

florrie 2万+

MCP的第一性原理:从工具调用到能力协议

这篇文章是MCP(Model Context Protocol)开发指南的摘要。MCP是一个用于连接AI模型与外部工具/服务的开放协议标准,由Anthropic提出。 主要内容包括: MCP协议概述 解决了AI应用与外部服务集成标准化问题,类似USB-C统一接口 采用Host-Server架构,支持双向通信 提供Tools(可调用操作)、Resources(可读取数据)、Prompts(提示词模板)三类能力 快速入门指南 提供5分钟快速上手指南 包含Python SDK安装和"Hello World"

sinat_28228747的博客 433

实时多目标跟踪(MOT):SORT与DeepSORT优化

实时多目标跟踪(MOT):SORT与DeepSORT优化​ ,人工智能,计算机视觉,大模型,AI,本文围绕实时多目标跟踪中的 SORT 与 DeepSORT 算法展开,阐述了两者原理、实现及优化。SORT 结合卡尔曼滤波和匈牙利算法,依赖目标检测,实时性好但复杂场景表现欠佳。DeepSORT 在其基础上引入外观特征,采用级联匹配,提升了跟踪精度和鲁棒性。还探讨了两种算法在检测、运动模型、外观特征等方面的优化方向,以及应用场景与选择建议,为相关应用和研究提供参考。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 20万+

机器学习kaggle报告-Facial Keypoints Detection【完整解题过程+代码】

在处理面部关键点检测任务时,首先需要将存储在 CSV 文件中的图像数据转换为可以显示和分析的图像。数据集中每个图像是以一个一维数组的形式存储,其中包含了图像的所有像素值。为了将这些数据转化为实际的图像,我们需要按照一定的规则将这些像素值重新排列为一个二维矩阵(即图像的行列)。下面是一个案例,展示了如何将数据集中的图像数据转化为图片。转化过程读取CSV文件:首先,我们需要读取包含图像数据的 CSV 文件,通常该文件包含多张图像的像素值。每一行表示一张图像,并且包含图像的像素值列表。提取图像数据。

一键难忘的博客 3353

c++中 Impl类说明:“接口”与“实现”分离的方式

接口与实现分离的两种方法

weixin_45022086的博客 6458

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

[设计模式] IMPL 模式

模块型代码中经常可以看到有一些类叫做 class Aimpl , 同时还会存在一个叫 class A的类,从名称上看 Aimpl 是 A 的 implement。实际上,A 往往是当前模块对外的 api 类,即其他模块如果需要使用当前模块,只需要实例化一个 A 就可以用了。那么 Aimpl 又是什么,如果观察代码就可以发现 A 的成员函数除了调用 Aimpl 的同名成员函数外,什么都没做,也就是说把逻辑实现部分放到了 impl 中,而对外暴露的接口则是 A。

ykun089的博客 1498

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

C++设计模式——Impl模式

以下内容来自C++ IMPL 模式解析(上)和C++ IMPL 模式解析(下),笔者仅在此基础上增加部分个人理解Impl是implement的缩写,意思为实现。在C++中,使用Impl模式,并不仅仅是为了将类的声明和定义放在不同的文件中,更重要的是隐藏类的实现细节和增加库的可维护性。本文描述了C++中Impl模式的三种实现方式,三种实现方式各有优劣。笔者本人拙见,第一种实现方式最简单,也足够使用(也许是因为笔者本人工程还不够)。

Dota_wtf的博客 748

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

C++ 中impl类是什么意思

"impl" 类是在 Rust 编程语言中的一种语法结构,表示实现(Implementation)。它用于实现某个 trait(特征)或者为结构体、枚举等数据类型提供方法。在 Rust 中,通过使用 impl 关键字可以让一个数据类型具有特定的行为,而不需要修改它的定义。 ...

weixin_42601608的博客 1402

使用模板方法模式封装协议消息

模板方法模式是设计模式中比较常用的一种,此模式就是提供了一个很好的代码复用平台,意在将不变的行为放到基类,去除子类中的重复代码。当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现,模板方法模式把这些行为搬移到单一的地方,这样就可以帮助子类拜托重复不变的行为的纠缠。由于类图太简单,就不单独画出,就一个基类和n个派生类,main函数中多态起对象(类似简单工厂模式),通过基类对象调用基类的固定方法,通过基类的固定方法调用虚函数实现多态的效果。

FlayHigherGT的博客 957
上一篇: [Rust] 实现SM4
scau_coderlin
博客等级 码龄3年 10粉丝 10原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值