一、线程的启动

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--线程管理的更多相关文章

  1. Java Concurrency in Practice 读书笔记 第十章

    粗略看完<Java Concurrency in Practice>这部书,确实是多线程/并发编程的一本好书.里面对各种并发的技术解释得比较透彻,虽然是面向Java的,但很多概念在其他语言 ...

  2. AngularJS in Action读书笔记6(实战篇)——bug hunting

    这一系列文章感觉写的不好,思维跨度很大,原本是由于与<Angularjs in action>有种相见恨晚而激发要写点读后感之类的文章,但是在翻译或是阐述的时候还是会心有余而力不足,零零总 ...

  3. Java并发读书笔记:线程安全与互斥同步

    目录 导致线程不安全的原因 什么是线程安全 不可变 绝对线程安全 相对线程安全 线程兼容 线程对立 互斥同步实现线程安全 synchronized内置锁 锁即对象 是否要释放锁 实现原理 啥是重进入? ...

  4. Java Concurrency in Practice——读书笔记

    Thread Safety线程安全 线程安全编码的核心,就是管理对状态(state)的访问,尤其是对(共享shared.可变mutable)状态的访问. shared:指可以被多个线程访问的变量 mu ...

  5. C#读书笔记:线程,任务和同步

    前言 学习C#两个多月了,像当初实习做PHP开发一样,也是由着一个个Feature需求,慢慢掌握了很多相关的编程技巧.本次主要记录下学习C# 多线程的相关知识. 参考书籍:<Csharp高级编程 ...

  6. 《CLR via C#》读书笔记 之 线程基础

    第二十五章 线程基础 2014-06-28 25.1 Windows为什么要支持线程 25.2 线程开销 25.3 停止疯狂 25.6 CLR线程和Windows线程 25.7 使用专用线程执行异步的 ...

  7. 《深入理解 Java 虚拟机》读书笔记:线程安全与锁优化

    正文 一.线程安全 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那 ...

  8. AngularJS in Action读书笔记4(实战篇)——创建Statistic模块

    个人感觉<Angularjs in action>这本书写的很好,很流畅,循序渐进,深入浅出,关键是结合了一个托管于Github上的实例讲解的,有代码可查,对于初学者应该是个不错的途径.( ...

  9. AngularJS in Action读书笔记2——view和controller的那些事儿

    今天我们来818<angularjs in action>的第三章controller和view. 1.Big Picture概览图 View是angularjs编译html后呈现出来的, ...

随机推荐

  1. Jenkins官方社区携手灵雀云成功举办Jenkins Meetup

    10月27日,由 Jenkins 官方社区和灵雀云主办的 Jenkins 插件开发 Meetup 在北京中科曙光大厦成功举办.这是Hacktoberfest(即 Hack October Festiv ...

  2. Mac下安装SecureCRT并激活

    今天花了好长的时间终于把SecureCRT安装成功了 现在分享给大家 安装的步骤, 希望对大家用帮助 Mac下的SecureCRT需要破解才能使用 所以有些费劲的.. 先下载SecureCRT和破解文 ...

  3. day19 python之re模块正则练习

    1.匹配标签 import re ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>" ...

  4. TypeScript之interface初探

    TypeScript的核心原则之一是对值所具有的结构进行类型检查,在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约. function printLabel(la ...

  5. win10配置环境变量

  6. kaggle learn python

    def has_lucky_number(nums): return any([num % 7 == 0 for num in nums]) def menu_is_boring(meals): &q ...

  7. TypeError: '_io.TextIOWrapper' object does not support item assignment

    纯小白 遇到的细节问题: 报错 一开始看到这个傻逼了 TypeError: '_io.TextIOWrapper' object does not support item assignment 其实 ...

  8. pickel加速caffe读图

    64*64*3小图(12KB),batchSize=128,训练样本100万, 全部load进来内存受不了,load一次需要大半天 训练时读入一个batch,ali云服务器上每个batch读入时间1. ...

  9. Description Resource Path Location Type Java compiler level does not match the version of(编译问题)

    project 编译问题,需要三处的jdk版本要保持一致,才能编译通过. 1.在项目上右键properties->project Facets->修改右侧的version  保持一致 2. ...

  10. vue-cli ——解决多次复用含有Echarts图表组件的问题

    在vue项目里,组件复用是一件很开心的事,可以节省很多时间去排版,达到事半功倍效果,但是昨晚在vue-cli项目里组件复用时发现基于Echarts图表的组件不能够复用,昨晚捯饬了很久,终于还是解决了这 ...