2.4在运行时选择线程数量
在运行时选择线程数量
C++标准库中对此有所帮助的特性是std::thread::hardware_currency()。这个函数返回一个对于给定程序执行时能够真正并发运行的线程数量的指示。例如,在多核系统上它可能是CPU 核心的数量。它仅仅是一个提示,如果该信息不可用则函数可能会返回0,但它对于在线程间分割任务是一个有用的指南。
清单2.8展示了std::accumulate 的一个简单的并行版本实现。它在线程之间划分所做的工作,使得每个线程具有最小数目的元素以避免过多线程的开销。请注意,该实现假定所有的操作都不引发异常,即便异常可能会发生。例如,std::thread构造函数如果不能启动一个新的执行线程那么它将引发异常。在这样的算法中处理异常超出了这个简单示例的范围。
//std::accumulate的简单的并行版本
#include <thread>
#include <numeric>
#include <algorithm>
#include <functional>
#include <vector>
#include <iostream>template<typename Iterator,typename T>
struct accumulate_block
{void operator()(Iterator first,Iterator last,T& result){result=std::accumulate(first,last,result);}
};template<typename Iterator,typename T>
T parallel_accumulate(Iterator first,Iterator last,T init)
{unsigned long const length=std::distance(first,last);if(!length)return init; //❶unsigned long const min_per_thread=25;unsigned long const max_threads=(length+min_per_thread-1)/min_per_thread; //❷unsigned long const hardware_threads= //❸std::thread::hardware_concurrency();unsigned long const num_threads=std::min(hardware_threads!=0?hardware_threads:2,max_threads);unsigned long const block_size=length/num_threads; //❹std::vector<T> results(num_threads);std::vector<std::thread> threads(num_threads-1); //❺Iterator block_start=first;for(unsigned long i=0;i<(num_threads-1);++i){Iterator block_end=block_start;std::advance(block_end,block_size); //❻threads[i]=std::thread( //❼accumulate_block<Iterator,T>(),block_start,block_end,std::ref(results[i]));block_start=block_end; //❽}accumulate_block<Iterator,T>()(block_start,last,results[num_threads-1]); //❾std::for_each(threads.begin(),threads.end(),std::mem_fn(&std::thread::join)); //❿return std::accumulate(results.begin(),results.end(),init); //⓫
} int main()
{std::vector<int> vi;for(int i=0;i<10;++i){vi.push_back(10);}int sum=parallel_accumulate(vi.begin(),vi.end(),5);std::cout<<"sum="<<sum<<std::endl;
}
虽然这是一个相当长的函数,但它实际上是很直观的。如果输入范围为空❶,只返回初始值init。否则,此范围内至少有一个元素,于是你将要处理的元素数量除以最小的块大小,以获取线程的最大数量❷。这是为了避免当范围中只有五个值时,在一个32核的机器上创建32个线程。
要运行的线程数是你计算出的最大值和硬件线程数量❸的较小值。你不会想要运行比硬件所能支持的更多的线程(超额订阅,oversubscription),因为上下文切换将意味着更多的线程会降低性能。如果对std::thread::hardware_concurrency()的调用返回0,你只需简单地替换上你所选择的数量,在这个例子中我选择了2。你不会想要运行过多的线程,因为在单核的机器上这会使事情变慢,但同样地你也不希望运行的过少,因为那样的话,你就会错过可用的并发。
每个待处理的线程的条目数量是范围的长度除以线程的数量❹。如果你担心数量不能整除,没必要——稍后再来处理。
既然你知道有多少个线程,你可以为中间结果创建一个 std::vector<T>
,同时为线程创建一个 std::vector<std::thread>
❺。请注意,你需要启动比
num_threads 少一个的线程,因为已经有一个了。
启动线程是个简单的循环:递进block_end迭代器到当前块的结尾❻,并启动一个新的线程来累计此块的结果❼。下一个块的开始是这一个的结束❽。
当你启动了所有的线程后,这个线程就可以处理最后的块❾。这就是你处理所有未被整除的地方。你知道最后一块的结尾只能是last,无论在那个块里有多少元素。一旦累计出最后一个块的结果,你可以等待所有使用std::for_each 生成的线程❿,如清单2.7中所示,接着通过最后调用std::accumulate将结果累加起来⓫。
在你离开这个例子前,值得指出的是在类型T的加法运算符不满足结合律的地方(如float和 double),这个parallel_accumulate的结果可能会跟std::accumulate的有所出入,这是将范围分组成块导致的。此外,对迭代器的需求要更严格一些,它们必须至少是前向迭代器(forward iterators),然而std::accumulate可以和单通输入迭代器(input iterators)一起工作,同时T必须是可默认构造的(default constructible)以使得你能够创建results向量。这些需求的各种变化是并行算法很常见的:就其本质而言,它们以某种方式的不同是为了使其并行,并且在结果和需求上产生影响。另外值得一提的是,因为你不能直接从一个线程中返回值,所以你必须将相关项的引用传入results向量中。从线程中返回结果的替代方法,会通过使用future来实现。
在这种情况下,每个线程所需的所有信息在线程开始时传入,包括存储其计算结果的位置。实际情况并非总是如此。有时,作为进程的一部分有必要能够以某种方式标识线程。你可以传入一个标识数,如同在清单2.7中 i 的值,但是如果需要此标识符的函数在调用栈中深达数个层次,并且可能从任意线程中被调用,那样做就很不方便。当我们设计C++线程库时就预见到了这方面的需求,所以每个线程都有一个唯一的标识符。
相关文章:
2.4在运行时选择线程数量
在运行时选择线程数量 C标准库中对此有所帮助的特性是std::thread::hardware_currency()。这个函数返回一个对于给定程序执行时能够真正并发运行的线程数量的指示。例如,在多核系统上它可能是CPU 核心的数量。它仅仅是一个提示,如果该信息不可用则函数可…...

element-ui中Notification 通知自定义样式、按钮及点击事件
Notification 通知用于悬浮出现在页面角落,显示全局的通知提醒消息。 一、自定义html页面 element-ui官方文档中说明Notification 通知组件的message 属性支持传入 HTML 片段,但是示例只展示了简单的html片段,通常不能满足开发中的更深入需要…...

无头单向非循环单链表、带头双向循环链表
文章内容 1. 链表的概念及结构 2. 链表的分类 3.链表实现 4.代码 文章目录 1. 链表的概念及结构 概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。 现实中 数据结构中 链表和顺序表…...
UE4/5C++多线程插件制作(二十、源码)
目录 头文件 MultiThreadPlugins.uplugin MultiThreadPlugins.Build.cs MultiThreadPlugins.h MTPPlatform.h MTPManage.h RTPAgendy.h MTPThreadTaskManage.h...

构建稳健的PostgreSQL数据库:备份、恢复与灾难恢复策略
在当今数字化时代,数据成为企业最宝贵的资产之一。而数据库是存储、管理和保护这些数据的核心。PostgreSQL,作为一个强大的开源关系型数据库管理系统,被广泛用于各种企业和应用场景。然而,即使使用了最强大的数据库系统࿰…...

查看本地mysql账号密码
使用Navicat工具打开本地mysql,新建查询输入下面查询语句 SELECT user, authentication_string FROM mysql.user WHERE userroot将authentication_string 中的加密密码复制出来打开链接: Magic Data 5输入加密的密码,和验证码,点…...

数据结构:顺序表详解
数据结构:顺序表详解 一、 线性表二、 顺序表概念及结构1. 静态顺序表:使用定长数组存储元素。2. 动态顺序表:使用动态开辟的数组存储。三、接口实现1. 创建2. 初始化3. 扩容4. 打印5. 销毁6. 尾插7. 尾删8. 头插9. 头删10. 插入任意位置数据…...

采集数据筛选-过滤不要数据或只保留指定数据
采集文章数据,有时候会遇到一些不需要采集的数据,或者只想采集一些特定的数据,可以使用简数采集器的内容过滤功能,对采集的数据进行筛选,只有符合的数据才采集保留。 可以用于过滤掉一些广告、专题、网站首页等无效数…...

RISC-V基础指令之shift移动指令slli、srli、srai、sll、srl、sra
RISC-V的shift指令是用于对一个寄存器或一个立即数进行位移运算,并将结果存放在另一个寄存器中的指令。位移运算就是把一个操作数的每一位向左或向右移动一定的位数,得到一个新的位。RISC-V的shift指令有以下几种: slli:左逻辑位…...

【沁恒蓝牙mesh】CH58x flash分区与数据存储管理
本文主要介绍了 沁恒蓝牙芯片 CH58x 的flash 分区与数据存储管理 📋 个人简介 💖 作者简介:大家好,我是喜欢记录零碎知识点的小菜鸟。😎📝 个人主页:欢迎访问我的 Ethernet_Comm 博客主页&…...

Ctfshow web入门 JWT篇 web345-web350 详细题解 全
CTFshow JWT web345 先看题目,提示admin。 抓个包看看看。 好吧我不装了,其实我知道是JWT。直接开做。 在jwt.io转换后,发现不存在第三部分的签证,也就不需要知道密钥。 全称是JSON Web Token。 通俗地说,JWT的本质…...

2023年国家留学基金委(CSC)青年骨干教师项目即将开始申报
国家留学基金委(以下简称CSC)的青年骨干教师出国研修项目(即高校合作项目),将于2023年9月10-25日进行网上报名及申请受理。知识人网小编特提醒申请者注意流程及政策,以防错过申报时间。 青年骨干教师项目&a…...

GC垃圾回收器【入门笔记】
GC:Garbage Collectors 垃圾回收器 C/C,手动回收内存;难调试、门槛高。忘记回收、多次回收等问题 Java、Golang等,有垃圾回收器:自动回收,技术门槛降低 一、如何定位垃圾? https://www.infoq.c…...

在 React 中渲染大型数据集的 3 种方法
随着 Web 应用程序变得越来越复杂,我们需要找到有效的方法来优化性能和渲染大型数据集。在 React 应用程序中处理大型数据集时,一次呈现所有数据可能会导致性能不佳和加载时间变慢。 虚拟化是一种通过一次仅呈现数据集的一部分来解决此问题的技术&#…...
uniapp iOS 消息推送扩展:后台/杀死app进程状态能语音播报
文章目录 引言I 前期准备1.1 配置扩展1.2 测试报文II iOS Extension(扩展)2.1 插件作者配置2.2 插件使用者配置see also引言 HBuilderX3.1.5+版本uni原生插件支持iOS Extension(扩展)。 消息推送离线语音播报插件获取方式: 公z号:iOS逆向: 离线包x10, 源码是x15。 实…...

批量创建可配置物料参数文件
启用可配置物料之后,每次创建新的物料需要通过CU41创建可配置物料,没找大批量创建的程序,所以SHDB录屏搞了一个代码。 前提:物料主数据初始化通过程序导入时,可配置物料参数文件已按照物料代码赋值。 效果…...

性能压力测试的重要性与实施方法
性能压力测试是在软件开发过程中评估系统在不同负载条件下的表现和稳定性的关键步骤。这种测试是为了确定系统在正常和峰值负载下的性能表现,以验证系统是否能够满足用户需求,同时发现潜在的性能问题并加以解决。 首先,性能压力测试对于确保系…...

HCIP入门静态实验
题目及要求 第一步:拓扑的搭建 第二步:路由、IP的配置 r1: <Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]sys r1 [r1]int loop [r1]int LoopBack 0 [r1-LoopBack0]ip add 192.168.1.65 27 [r1-LoopBack0]int loop 1 […...
Vue与js的融合,如何编写现代化的前端应用
随着Web应用的不断发展,前端开发已经成为了当今互联网行业中最为流行和重要的领域之一。而在前端开发中,JavaScript无疑是最为常用和基础的语言之一。而Vue.js作为一种轻量级的JavaScript框架,它的出现极大地简化了前端开发的过程,…...
Boost开发指南-3.10singleton_pool
singleton_pool singleton_pool与 pool的接口完全一致,可以分配简单数据类型(POD)的内存指针,但它是一个单件。 singleton_pool位于名字空间boost,为了使用singleton_pool组件,需要包含头文件<boost/p…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...

短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...