《c++ concurrency in action》读书笔记2--线程管理
一、线程的启动
1. 每个c++程序至少有一个线程,是由C++ runtime启动的
2. 在c++11中,通过一个std::thread 对象启动线程。可以向std::thread传递一个函数,或者实现了调用操作符的类实例, 或者一个lambda表达式。
// 方法一
void do_some_work();
std::thread my_thread(do_some_work); // 方法二
class background_task
{
public:
void operator()() const
{
do_something();
do_something_else();
}
};
background_task f;
std::thread my_thread(f); // 方法三
std::thread my_thread([](
do_something();
do_something_else();
}); // 方法四
class X
{
public:
void do_lengthy_work();
};
X my_x;
std::thread t(&X::do_lengthy_work,&my_x);
传递给std::thread的函数对象,会被拷贝到线程对象的上下文中,所以需确保拷贝后的对象和源对象一致。
注意,当使用临时对象初始化std::thread对象时:
std::thread my_thread(background_task()); // 声明了一个函数,而不是初始化了一个线程对象。 // 正确的方法:
std::thread my_thread(background_task());
std::thread my_thread(background_task{});
3. 线程启动后,需要显示的决定是等待其执行完成(join) 或者让它自己运行(detach()), 如让线程自己运行,需确保线程访问的数据在结束之前是一直有效的。不然会出现未定义的行为。调用join()函数后,std::thread对象会清理所有存储的变量,所以对一个线程来说,join()函数只能调用一次,可以通过joinable()函数来检查是否可以调用join.
4. 为了避免程序终止,需要调用join/detach, 对于detach来说,可以在std::thread对象初始化后就调用,而对于join来说,则需要注意需要确保所有退出通路都调用join(), RAII(Resource Acquisition Is Initialization) 原则,则是一个不错的方法。
class thread_guard
{
std::thread& t;
public:
explicit thread_guard(std::thread& t_):
t(t_)
{}
~thread_guard()
{
if (t.joinable())
{
t.join();
}
} thread_guard(thread_guard const&)=delete;
thread_guard& operator=(thread_guard const&)=delete;
}; struct func;
void f() {
int some_local_state=0;
func my_func(some_local_state);
std::thread t(my_func);
thread_guard g(t);
do_something_in_current_thread();
}
上面的scoped_thread类的构造函数使用的是引用,而使用引用可能存在一个问题,即std::thread的对象可以在多个地方被析构,而一个std::thread对象只能被move不能被copy,所以这里可以传一个非引用
class scoped_thread
{
std::thread t;
public:
explicit scoped_thread(std::thread t_):
t(std::move(t_))
{
if(!t.joinable())
throw std::logic_error(“No thread”);
} ~scoped_thread()
{
t.join();
}
scoped_thread(scoped_thread const&)=delete;
scoped_thread& operator=(scoped_thread const&)=delete;
}; struct func;
void f()
{
int some_local_state;
scoped_thread t(std::thread(func(some_local_state)));
do_something_in_current_thread();
}
5. 调用detach()后,线程会在背景运行。一个线程对象,一旦是detach状态,就不能通过std::thread对象来管理它,detach后线程的控制权将会转移到c++运行库,c++运行库会确保线程退出时相应的资源被正确的释放。而对于没有运行的std::thread对象,是不能调用detach()和join()的。
6. 一个detach使用的例子
void edit_document(std::string const& filename)
{
open_document_and_display_gui(filename);
while(!done_editing())
{
user_command cmd=get_user_input();
if(cmd.type==open_new_document)
{
std::string const new_name=get_filename_from_user();
std::thread t(edit_document,new_name);
t.detach();
}
else
{
process_user_input(cmd);
}
}
}
二、向线程函数传递参数
1. 向线程函数传递参数是非常简单的,即将额外的参数传递到std::thread的构造函数中。需要注意的是,默认情况下,参数将被拷贝到线程内部的存储中,
void f(int i,std::string const& s);
std::thread t(f,3,"hello");
在上面的例子中,char const* 类型会在线程的上下文中完成向std::string对象的转化。
void f(int i,std::string const& s);
void not_oops(int some_param)
{
char buffer[1024];
sprintf(buffer,"%i",some_param);
// std::thread t(f,3,buffer); 这种方法可能存在 not_oops函数先退出,然后 buffer才被转化为std::string的情况,这种情况的行为是未定义的。应该显示转为string
std::thread t(f,3,std::string(buffer));
t.detach();
}
当希望将参数做为引用传递时,使用std::ref
void update_data_for_widget(widget_id w,widget_data& data);
void oops_again(widget_id w)
{
widget_data data;
std::thread t(update_data_for_widget,w,std::ref(data));
display_status();
t.join();
process_widget_data(data);
}
2. 对于只能move的对象, 对于临时对象,会自动使用move, 对于非临时对象,则需要显示的调有move
void process_big_object(std::unique_ptr<big_object>);
std::unique_ptr<big_object> p(new big_object);
p->prepare_data(42);
std::thread t(process_big_object,std::move(p));
三、转移线程对象的所有全
1. std::thread对象只支持move操作,不支持copy操作。
void some_function();
void some_other_function();
std::thread t1(some_function);
std::thread t2=std::move(t1);
t1=std::thread(some_other_function);
std::thread t3;
t3=std::move(t2);
t1=std::move(t3); // return from a function
std::thread f()
{
void some_function();
return std::thread(some_function);
}
std::thread g()
{
void some_other_function(int);
std::thread t(some_other_function,42);
return t;
} // pass to a function
void f(std::thread t);
void g()
{
void some_function();
f(std::thread(some_function));
std::thread t(some_function);
f(std::move(t));
}
2. 等待多个线程完成
void do_work(unsigned id); void f() {
std::vector<std::thread> threads;
for(unsigned i=0;i<20;++i)
{
threads.push_back(std::thread(do_work,i));
}
// std::mem_fn -> 生成指向成员指针的包装对象
std::for_each(threads.begin(),threads.end(),
std::mem_fn(&std::thread::join));
}
四 运行时选择线程数量
1. std::thread::hardware_concurrency() 返回一个执行程序真正可以并发的数量。(比如,在一个多核系统中,返回CPU的核数)但这只是一个提示,函数的返回值有可能为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)
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);
}
五 获取线程标示
1. 可以通过 std::this_thread::get_id() 获取线程标示(为std::thread::id对象,如果两个std::thread::id对象相等,则表示同一个线程或者 “not any thread”.
std::thread::id master_thread;
void some_core_part_of_algorithm()
{
if(std::this_thread::get_id()==master_thread)
{
do_master_thread_work();
}
do_common_work();
}
《c++ concurrency in action》读书笔记2--线程管理的更多相关文章
- Java Concurrency in Practice 读书笔记 第十章
粗略看完<Java Concurrency in Practice>这部书,确实是多线程/并发编程的一本好书.里面对各种并发的技术解释得比较透彻,虽然是面向Java的,但很多概念在其他语言 ...
- AngularJS in Action读书笔记6(实战篇)——bug hunting
这一系列文章感觉写的不好,思维跨度很大,原本是由于与<Angularjs in action>有种相见恨晚而激发要写点读后感之类的文章,但是在翻译或是阐述的时候还是会心有余而力不足,零零总 ...
- Java并发读书笔记:线程安全与互斥同步
目录 导致线程不安全的原因 什么是线程安全 不可变 绝对线程安全 相对线程安全 线程兼容 线程对立 互斥同步实现线程安全 synchronized内置锁 锁即对象 是否要释放锁 实现原理 啥是重进入? ...
- Java Concurrency in Practice——读书笔记
Thread Safety线程安全 线程安全编码的核心,就是管理对状态(state)的访问,尤其是对(共享shared.可变mutable)状态的访问. shared:指可以被多个线程访问的变量 mu ...
- C#读书笔记:线程,任务和同步
前言 学习C#两个多月了,像当初实习做PHP开发一样,也是由着一个个Feature需求,慢慢掌握了很多相关的编程技巧.本次主要记录下学习C# 多线程的相关知识. 参考书籍:<Csharp高级编程 ...
- 《CLR via C#》读书笔记 之 线程基础
第二十五章 线程基础 2014-06-28 25.1 Windows为什么要支持线程 25.2 线程开销 25.3 停止疯狂 25.6 CLR线程和Windows线程 25.7 使用专用线程执行异步的 ...
- 《深入理解 Java 虚拟机》读书笔记:线程安全与锁优化
正文 一.线程安全 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那 ...
- AngularJS in Action读书笔记4(实战篇)——创建Statistic模块
个人感觉<Angularjs in action>这本书写的很好,很流畅,循序渐进,深入浅出,关键是结合了一个托管于Github上的实例讲解的,有代码可查,对于初学者应该是个不错的途径.( ...
- AngularJS in Action读书笔记2——view和controller的那些事儿
今天我们来818<angularjs in action>的第三章controller和view. 1.Big Picture概览图 View是angularjs编译html后呈现出来的, ...
随机推荐
- Linux系统——MHA-Atlas-MySQL高可用集群
Linux系统——MHA-Atlas-MySQL高可用集群 MHA MHA介绍MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,是一套优秀的 ...
- 排序之选择排序(SelectSort)
package com.sort; /* * 选择排序 * 把第一位与其他数进行比较,这样每轮比较都会出现一个最大值或最小值 * 根据需要让升序或降序排列 */ public class Select ...
- asp.net导入后台代码
public void Upload(string information){ int Bank = 0; for (int i = 0; i <Request.Files.Count; i++ ...
- 关于webpack官网的学习
webpack,从名词上,"web pack",大概可以看出是一个网页打包工具,其实它具有打包.压缩.解析编译的功能. 使用(配置webpack.config.js) entry: ...
- Mysql AVG() 值 返回NULL而非空结果集
[1]select 查询返回一行NULL 先来模拟复现一下遇到的问题. (1)源数据表grades,学生成绩表 (2)查询SQL语句 查询‘080601’班的各门课平均成绩 SELECT sClass ...
- echarts 图的点击事件(含:点击重复触发的问题及其解决方法)
今天用echarts的时候发现一个问题 鼠标指向不同地市触发一个事件展示该地区趋势图 但是但是后台中不管我第几次鼠标指向都会触发两次指向事件 现在贴出解决办法: 问题完美解决.但是为什么会调用两次, ...
- 2018-2019-2 网络对抗技术 20165335 Exp4 恶意代码分析
实验内容: 一.使用schtacks进行系统运行监控,使用sysmon工具监控系统的具体进程,使用各种工具进行监控,并针对软件的启动回连,安装到目标机,以及其他的控制行为的分析,同时,对主机的注册表, ...
- MQ知识点汇总
1. MQ是什么 2. MQ能做什么 3. 消息模式 4. 使用MQ的时候需要注意什么 5. 常用MQ 6. MQ的不足 7. 什么时候不适用MQ 8. MQ的组成 9. MQ的关注点 1. MQ是什 ...
- Android的JSON数据解析
一. 使用原生方式解析 准备工作:准备一个布局文件,用来显示元数据与转换之后的数据 <?xml version="1.0" encoding="utf-8" ...
- ltp-ddt nor qspi spi调试中需要修改的地方
1 blk_device_dd_readwrite_test.sh before SRC_FILE="/home/root/srctest_file_${DEVICE_TYPE}_$$&qu ...