参考《c++ Concurrency In Action 》第二章做的笔记

目录

  • 传递参数
  • 量产线程
  • 线程标识

传递参数

thread构造函数的附加参数会拷贝至新线程的内存空间中,即使函数中的采纳数是引用类型,拷贝操作也会执行。如果我们期待传入一个引用,必须使用std::ref将参数转换成引用形式:

如下:

void update_weight(weight_id w,weight_data& data);   //1
void oops(weight_id w)
{weight_data data;//错误方式std::thread t(update_weight,w,data);//正确方式std::thread t(update_weight,w,std::ref(data));display_status();t.join();
}

这样就能收到data的引用,而非data的拷贝副本。

std:bind的传参机制相同,使用std::thread创建线程时,传递参数的过程如下:

  • std::thread构造函数传参:一般实参会被拷贝至新线程的内存空间。具体拷贝的过程是由调用线程(主线程)在堆上创建并交由子线程管理,在子线程结束时同时被释放。
  • 向线程函数传参:由于std::thread对象里一般保存的是参数的副本,为了效率同时兼顾一些只移动类型的对象,所有的副本均被std::move到线程函数,即以右值的形式传入。

示例:std::move转移动态对象的所有权到线程中去:

void process_big_object(std::unique_ptr<big_object>);std::unique_ptr<big_object> p(new big_object);
p->prepare_data(47);
std::thread t(process_big_object,std::move(p));

在thread构造函数中执行move,big_object对象的所有权首先转移到新创建线程的内部存储中,之后再传递给process_big_object函数。

量产线程

void do_work(unsigned id);void f()
{std::vector<std::thread> threads;for(unsigned i = 0; i < 20; i++)threads.emplace_back(do_work,i);  //产生线程for(auto& entry : threads)                //对每个线程调用join()entry.join();
}

使用线程去分割一个算法的工作总量,所以在算法结束之前,所有线程必须结束。线程所做的工作都是独立的,并且结果仅会受到共享数据的影响。如果f有返回值,在写入返回值之前,程序会检查使用共享数据的线程是否终止。

下面函数,会返回并发线程的数量

std::thread::hardware_concurrency()

下面展示并行版本的std::accumulate。代码将整体工作拆分成小任务,交给每个线程去做,并设置最小任务数,避免产生太多的线程,程序会在操作数量为0时抛出异常。

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)        //1return init;unsigned long const min_per_thread = 25;unsigned long const max_threads = (length+min_per_thread-1)/min_per_thread;       //2unsigned long const hardware_threads = std::thread::hardware_concurrency();unsigned long const num_threads =                               //3std::min(hardware_threads != 0 ? hardware_threads : 2,max_threads);unsigned long const block_size = length / num_threads;          //4std::vector<T> results(num_threads);std::vector<std::thread> threads(num_threads - 1);               //5Iterator block_start = first;for(unsigned long i = 0; i < num_threads-1; ++i){Iterator block_end = block_start;std::advance(block_end,block_size);                           //6threads[i] = std::thread(                                   //7accumulate_block<Iterator,T>(),block_start,block_end,std::ref(result[i]));block_start = block_end;                                    //8}accumulate_block<Iterator,T>(),block_start,last,results[num_threads-1]);              //9for(auto& entry : threads)entry.join();                                              //10return std::accumulate(results.begin(),results.end(),init);     //11
}

函数解释:

如果输入范围为空1,就会得到init值。如果范围内的元素多于1个,需要用范围内元素的总数量除以线程块中最小任务数,从而确定启动线程的最大数量。由于上下文切换可能会降低线程性能,所以计算最大线程数以及硬件支持线程数,选择较小值作为启动线程数量3。如果为0的话,选择2替代。

每个线程中处理的元素数量,是范围中元素的总量除以线程的个数得出的4。

既然确定了线程个数,创建一个std::vector<T>容器存放中间结果,并为线程创建一个std::vector<std::thread>容器。因为在启动之前已经有了一个主线程,所以启动线程数是num_threads-1。

使用循环启动线程,block_end指向当前块的末尾6,并启动一个新线程为当前块累加结果7.当迭代器指向当前块的末尾时,启动下一个块8.

启动所有县城后,9中线程会处理最终块的结果,累加最终块结果后,等待std::for_each创建线程10.之后使用std::accumulate将所有结果进行累加11

线程标识

线程标识为std::thread::id类型,可以通过成员函数get_id()来获取。如果thread对象没有和任何执行线程相关联,将返回一个默认构造值,表示“无线程”。

如果两个线程id相等,说明是同一个线程,或者都是“无线程”。

std::thread::id实例常用作检测线程是否需要进行一些操作,比如,当用线程来分割一项工作,主线程可能要做一些与其他线程不同的工作,启动其他线程前,可以通过get_id得到自己线程id,每个线程都检查一下,其拥有线程id是否与初始线程id相同。

std::thread::id master_thread;
void some_core_part_of_algorithm()
{if(std::this_thread::get_id() == master_thread)do_master_thread_work();elsedo_common_work();
}

《线程管理:传递参数、确定线程数量、线程标识》相关推荐

  1. ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  2. ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  3. 信息学奥赛真题解析(玩具谜题)

    玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...

  4. 信息学奥赛之初赛 第1轮 讲解(01-08课)

    信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...

  5. 信息学奥赛一本通习题答案(五)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  6. 信息学奥赛一本通习题答案(三)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  7. 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题

    第1章   快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章  素数 第 3 章  约数 第 4 章  同余问题 第 5 章  矩阵乘法 第 6 章 ...

  8. 信息学奥赛一本通题目代码(非题库)

    为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...

  9. 信息学奥赛一本通(C++版) 刷题 记录

    总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...

  10. 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离

    首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...

最新文章

  1. CKEditor的安装与基本使用
  2. Java HashMap涉及的数据结构及实现
  3. 使用typedef简化函数指针的声明
  4. 【CF1194E】Count The Rectangles【类扫描线】【单调性】【树状数组】
  5. 配置java ee_Java EE中的配置管理
  6. 2018.12.11——全局变量与局部变量
  7. 系统学习机器学习之神经网络(十一) --TDNN
  8. iOS底层探索之类的加载(三): attachCategories分析
  9. NOI2019游记 —— 夏花般绚烂,繁星般璀璨
  10. 第四次作业——04树
  11. 【蓝牙串口无线烧写程序】适用于STM32F103和STM32F107的Bootloader
  12. AD2016 交互式网表 InteractiveHtmlBomForAD插件安装教程
  13. Ubuntu 20.04 离线安装podman
  14. Qt容器:QList
  15. c语言解除指针引用什么意思,“解引用”指针是什么意思?
  16. 笔记本虚拟机 安装红旗linux x86,在红旗linux中安装vmware虚拟机
  17. android 调用系统文件管理器
  18. 特征匹配中的欧氏距离
  19. 个股牛市的条件(1)
  20. 李宏毅DLHLP.10.Voice Conversion.2/2. CycleGAN and starGAN

热门文章

  1. 【APICloud系列|13】移动端适配通揽
  2. html 文本框 自动拼接,HTML 中table的结构以及拼接
  3. android读取excel文件_python里读写excel等数据文件的几种常用方式
  4. sinaapp mysql连接_手把手教你在新浪云上免费部署自己的网站--连接数据库
  5. React 学习笔记 —— Ref Hook
  6. [前端优化]使用Microsoft Ajax Minifier对资源文件进行压缩优化
  7. 有趣的js匿名函数写法(function嵌套)
  8. 文档声明和HTML样式表
  9. 关于数据库名、实例名
  10. 后台通过request.setAttribute向前台传值,前台如何去获取其中的对象或属性值