一步一步写线程之八线程池的完善之二数据结构封装
一、数据容器
在前面分析过,不管是线程任务的封装还是同步数据队列的封装,都是需要一个数据结构的。一用来说,如果没有什么特殊的原因,开发者都是使用STL中数据结构。比如前面经常见到的std::queue,std::deque,std::vector,std::unordered_map,std::list等等。
但是大家都知道,标准库中的容器一般都是非线程安全的,所以在线程池中如果想使用这些容器,就需要自己处理一下相关的线程安全相关的控制。
二、线程的安全性
所谓线程的安全数据访问,其实在前面学习了很多方法和技术。在不同的平台上,可能也有不小的差异。但标准库的出现还是让这种差异弥合了很多。一般来说,在保证线程数据安全访问,也就是同步控制上,使用锁。也就是lock来锁住mutex。
但如何安全的使用锁,这就有很多技巧了。包括锁的粒度的大小,锁使用的时机和应用的地方。在下面的例子中,大家可以分析一下,如何进行更好的优化。
三、接口封装
数据结构如果使用标准库的容器,就会有一个问题,标准库的不同的容器,其对外的接口的名称是不相同的。当然,如何不对这些容器进行泛化,只是单纯的使用某种容器,就没有这种问题。但是如何想使用模板来整体使用这些容器呢?
这就需要一些辅助的技术来控制,既要达到在上层的接口统一性,又要保证在调用下层的库接口时的安全。这时,就用到了模板的SFINAE技术,当然,在高版本中,也可以使用Concepts(概念)。
四、实例
通过上面的分析,基本明白了这个抽象的数据结构封装的方式,其实就是在STL库的上层再封装一层,同时要保证多线程访问时的安全性,看下面的例子:
1、首先对原TaskQueue进行改造:
#ifndef __TASKBUCKET_H__
#define __TASKBUCKET_H__#include "common.h"
#include <iostream>
#include <mutex>
#include <queue>template <typename T, typename Con = std::queue<T>> class TaskBucket final : public NoCopy {
public:TaskBucket() {}~TaskBucket() {}public:void Pop() { this->queue_.pop(); }void Push(T t) {std::cout << "Push bool:" << HasMemFuncCpp11<Con>::value << std::endl;std::unique_lock lock(this->mu_);if constexpr (HasMemFuncCpp11<Con>::value) {this->queue_.emplace_back(t);}}T Front() {std::unique_lock lock(this->mu_);return this->queue_.front();}T Back() {std::unique_lock lock(this->mu_);return this->queue_.back();}auto PopFront() {std::cout << "PopFront bool:" << HasMemFunc<Con>::value << std::endl;std::unique_lock lock(this->mu_);if constexpr (HasMemFunc<Con>::value) {T t = this->queue_.front();this->queue_.pop_front();return t;}}T PopBack() {std::cout << "PopBack bool:" << has_member_pop_back<Con>::value << std::endl;if constexpr (hasmem_pop_back<Con>::value) {std::unique_lock lock(this->mu_);T t = this->queue_.back();this->queue_.pop_back();return t;}}private:std::mutex mu_;Con queue_;
};
上面的代码主动对三部分进行了处理,一部分是增加了类本身不允许拷贝;另外一个增加了对容器的再次抽象,即容器可以动态传入;第三部分就是增加了对容器的成员函数通过SFINAE进行了判断。这也是模板技术在实际场景中应用的一种方式,下面会对其进行分析。需要注意的是,这里使用的c++17以上的编译环境。下面看一下相关的代码:
#ifndef __COMMON_H__
#define __COMMON_H__#include <functional>
#include <utility>
using CallBackMsg = std::function<void(int *, int)>;
using Task = std::function<void(int)>;class NoCopy {
protected:NoCopy() = default;~NoCopy() = default;public:NoCopy(const NoCopy &) = delete;NoCopy &operator=(const NoCopy &) = delete;NoCopy(NoCopy &&) = delete;NoCopy &operator=(NoCopy &&) = delete;
};
//第一种方式
template <typename T> struct HasMemFunc {
private:template <typename U> static auto Check(int) -> decltype(std::declval<U>().pop_front(), std::true_type());template <typename U> static std::false_type Check(...);public:enum { value = std::is_same<decltype(Check<T>(0)), std::true_type>::value };
};//Muduo库的使用方式(和上面类似):
template <typename T> struct hasPopFront {template <typename C> static char Check(decltype(&C::pop_front));template <typename C> static int32_t Check(...);const static bool value = sizeof(Check<T>(0)) == 1;
};//第二种方式
template <typename T> using Type = void;//处理无参
template <typename T, typename V = void> struct HasPop : std::false_type {};
template <typename T> struct HasPop<T, Type<decltype(std::declval<T>().pop_front())>> : std::true_type {};
//第二种方式-c++11后
template <typename T, typename V = void> struct HasMemFuncCpp11 : std::false_type {};template <typename T>
struct HasMemFuncCpp11<T, Type<decltype(std::declval<T>().emplace_back(std::declval<typename T::value_type>()))>>: std::true_type {};//第三种方式
#define HAS_MEM(mem) \template <typename T, typename... Args> struct hasmem_##mem { \private: \template <typename U> \static auto Check(int) -> decltype(std::declval<U>().mem(std::declval<Args>()...), std::true_type()); \template <typename U> static std::false_type Check(...); \\public: \enum { value = std::is_same<decltype(Check<T>(0)), std::true_type>::value }; \};
HAS_MEM(pop)
HAS_MEM(pop_back)
HAS_MEM(push)
#endif // __COMMON_H__
在上面代码中主要分成了三种形式,第一种和第二种是使用检查函数是否存在的普通方式,虽然看上去是使用了模板,但对成员函数名称还是进行了限制。所以如果需要限制多种成员函数还得手动写多个类;第三种使用宏的方式自动产生相关的模板代码,这样会少写不少的重复的类似的代码。
这三种SFINAE的原理基本是一致的,虽然实现的手段有细节的不同。但都是通过decltype和std::declval(二者的区别可以查看前面的相关文章),通过是否能够符合模板自动匹配(即能否正常产生相关特化模板)来返回true 或false的value来达到判断是否存在函数的目的,然后通过if constexpr在编译期进行处理。Muduo库的使用方式前面分析过(“SFINAE的技巧应用”),基本原理也是如此,只是直接使用定义的函数匹配来得到值而非使用std::true_type这种形式,但逻辑是一样的。
至于是否使用 std::is_same_v来替代 std::is_same,就看个人的兴趣了。
大家分析的时候儿可以从第一种开始,简单的按分析说明一对就明白了,然后再到第二种,特别是Type类型能否获取,是value值的关键。到第三种基本就是前面的抽象版,理解了前面的两种方式,第三种就非常简单了。
此处的封装未必是要求把所有的容器都封装起来,但它可以适应所有容器,什么意思呢?开发者可以根据自己的情况来选取指定的一两种容器来使用。当然,在实际的应用场景中,可能更多的是直接使用容器。这里之所以封装起来,就是为了给出一个更搞抽象的思路和方法。这点对于自己写容器而不使用STL中的容器的情况下更有意义。
随着GCC等编译器对c++20支持的逐渐普及化。估计明后年,可能就可以普遍的使用c++20编程了(ubuntu22默认的g++11对c++20已经支持了大部分),这样就可以使用Concept以及其它相关的新特性,开发工作会变得更简单一些。
五、总结
在分析完成数据结构的封装后,基本也就明白了在多线程中如何使用STL中的容器。但是,一般对线程安全的操作控制,都是使用锁。不管锁的粒度是大是小,对性能的影响一般来说都是比较大的。而多线程特别是线程池恰恰又都是在高性能的场景下应用,所以这时就需要开发者认真考虑加锁引起的性能损失。
当然,在后面可以考虑在某些情况下使用无锁编程技术,让数据的处理更快捷。但无锁编程也不是万能的,它也是有其相对的应用场景。万流归宗,还是需要开发者对整体技术的把握和实际应用场景进行综合考虑。
相关文章:
一步一步写线程之八线程池的完善之二数据结构封装
一、数据容器 在前面分析过,不管是线程任务的封装还是同步数据队列的封装,都是需要一个数据结构的。一用来说,如果没有什么特殊的原因,开发者都是使用STL中数据结构。比如前面经常见到的std::queue,std::deque,std::vector,std::…...
go连接数据库(原生)
根据官网文档 Go Wiki: SQL Database Drivers - The Go Programming Language 可以看到go可以连接的关系型数据库 常用的关系型数据库基本上都支持,下面以mysql为例 下载mysql驱动 打开上面的mysql链接 GitHub - go-sql-driver/mysql: Go MySQL Driver i…...
【C语言】2048小游戏【附源码】
欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 一、游戏描述: 2048是一款数字益智类游戏,玩家需要使用键盘控制数字方块的移动,合并相同数字的方块,最终达到数字方块上出现“2048”的目标。 每次移动操作,所…...
部署项目遇到的各种问题总结
文章目录 前言一、后端问题 jar包运行出现错误宝塔面板使用jdk17二、数据库问题 版本问题三、前端问题 连不上后端总结 前言 在做完项目之后,为了让别人访问到自己的网站,就需要部署前端后端以及数据库,但是在部署的过程中出现了各种问题和困…...
JavaSE:抽象类和接口
目录 一、前言 二、抽象类 (一)抽象类概念 (二)使用抽象类的注意事项 (三)抽象类的作用 三、接口 (一)接口概念 (二)接口语法规则 (三&a…...
发票是扫码验真好,还是OCR后进行验真好?
随着科技的进步,电子发票的普及使得发票的验真方式也在不断演进。目前,我们常见的发票验真方式主要有两种:一种是扫描发票上的二维码进行验真,另一种是通过OCR(Optical Character Recognition,光学字符识别…...
【AIGC调研系列】AIGC+Jmeter实现接口自动化测试脚本生成
AIGC(人工智能生成内容)结合JMeter实现接口自动化测试脚本生成的方法,主要涉及到通过流量收集工具和AIGC技术获取用户操作接口数据,并利用这些数据生成自动化测试脚本的过程。这种方法可以有效提高软件测试的效率和质量[1]。JMete…...
前端|babel升级
问题 项目不支持可选链调用过多的 babel 插件 步骤 基础包 dependencies “react-scripts”: “5.0.1” devDependencies “customize-cra”: “^1.0.0”,“react-app-rewired”: “^2.2.1”, 框架包 dependencies “react”: “16.13.1”,“react-dom”: “16.13.1”, …...
【微服务】spring状态机模式使用详解
一、前言 在很多系统中,通常会涉及到某个业务需要进行各种状态的切换操作,例如在审批流程场景下,某个审批的向下流转需要依赖于上一个状态的结束,再比如电商购物场景中,一个订单的生命周期往往伴随着不同的状态&#…...
【算法刷题day14】Leetcode:144.二叉树的前序遍历、94.二叉树的中序遍历、145.二叉树的后序遍历
文章目录 二叉树递归遍历解题思路代码总结 二叉树的迭代遍历解题思路代码总结 二叉树的统一迭代法解题思路代码总结 草稿图网站 java的Deque 二叉树递归遍历 题目: 144.二叉树的前序遍历 94.二叉树的中序遍历 145.二叉树的后序遍历 解析:代码随想录解析…...
mysql闲谈
如何定位慢查询 1、测试环境压测时,有的接口非常慢,响应时间超过2秒以上。当时系统部署了运维的监控系统Skywalking,在展示报表中可以看到是哪儿个接口慢,可以看到SQL具体执行时间。 2、如果没有类似的监控系统,在Mysq…...
物联网学习1、什么是 MQTT?
MQTT(Message Queuing Telemetry Transport)是一种轻量级、基于发布-订阅模式的消息传输协议,适用于资源受限的设备和低带宽、高延迟或不稳定的网络环境。它在物联网应用中广受欢迎,能够实现传感器、执行器和其它设备之间的高效通…...
【机器学习】数据探索(Data Exploration)---数据质量和数据特征分析
一、引言 在机器学习项目中,数据探索是至关重要的一步。它不仅是模型构建的基础,还是确保模型性能稳定、预测准确的关键。数据探索的过程中,数据质量和数据特征分析占据了核心地位。数据质量直接关系到模型能否从数据中提取有效信息ÿ…...
软件测试(一)--简介+主流技能+分类+模型+流程
一、软件及测试简介 1、软件生产过程 需求产生–需求文档–设计效果图–产品开发–产品测试(测试产品与需求文档是否一致)–部署上线 2、什么是软件测试 使用技术手段验证软件是否满足使用需求。 技术包括:(使用网络技术测试安…...
技术引领,策略升级:腾讯云与你共探数字金融新篇章
引言 2024 年 3 月 27 日下午,在北京腾讯总部,一场关于大模型与数据要素时代数字金融发展的深入讨论火热进行中。【TVP 走进腾讯:大模型与数据要素时代的数字金融发展论坛】是在腾讯二十年发展历程和数字化实践的基础上,进一步探索…...
数据库-root密码丢失的重置方案(win11环境)
当在windows系统中安装的mysql由于操作不当,或者密码遗忘,今天测试了一下,可以用以下方法重置root的密码。 mysqlwindows环境root密码重置问题 在win10/11环境下mysql8密码遗忘后的重置密码方案。 停止mysql服务 查找windows中的mysql服务名称…...
免试生常问的一些问题汇总---专升本学习篇
1.你怎么理解你申请的专业? 答:软件工程室一门涉及软件开发、维护和管理的工程学科。它结合了计算机科学、工程学和管理科学的原理,皆在通过系统化、规范化的方法来开发高质量的软件系统。 1.技术和支持 :软件工程包括编程语言、算法、数据结构、软件设计模式、软件测试、…...
FPGA的就业前景
FPGA(Field-Programmable Gate Array)技术在数字电路设计和嵌入式系统开发方面具有广泛的应用,因此在FPGA领域有着较好的就业前景。 目前,FPGA在通信、计算机、消费电子、汽车、航空航天等行业中得到了广泛应用。随着新一代通信网…...
7.阻塞模式与非阻塞模式
1.阻塞模式 一个线程来处理多个连接显得力不从心 accept等待连接 是一个阻塞方法 read读取SocketChannel中的数据 是一个阻塞方法 /*** 服务端* param args* throws IOException*/public static void main(String[] args) throws IOException {//建立一个缓冲区ByteBuffer b…...
Unity类银河恶魔城学习记录11-10 p112 Items drop源代码
Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili ItemObject_Trigger.cs using System.Collections; using System.Collecti…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
