C++11 并发实战阅读笔记(1)
来源:互联网 发布:汽车配件淘宝店铺名字 编辑:程序博客网 时间:2024/04/29 22:18
第二章 managing threads
std::thread 的用法:
void do_some_work(); std::thread my_thread(do_some_work);
还可以使用函数对象,函数对象就是重载了()操作符的类,可以用来形如 A() 来达到调用函数的效果
class background_task{public:void operator()() const{ do_something(); do_something_else();}};background_task f;std::thread my_thread(f); // f()就表示调用了()操作符,这里就相当于执行代码do_something()和do_something_else()
这种用法有个陷进:
std::thread my_thread(background_task()); // 这里不会启动线程,而是将my_thread当成了一个返回thread对象的函数这个陷阱叫做“C++’s most vexing parse”, 具体解释可以见Effective STL 或者如下链接
http://www.cnblogs.com/yangyingchao/p/3394146.html
解决办法:
std::thread my_thread((background_task()));std::thread my_thread{background_task()}; // C++11 变量的初始化方法
或者使用C++11里的lambda函数:
std::thread my_thread([]{(do_something();do_something_else();});
list 2.1: 线程对函数局部变量的访问非法
struct func{ int& i; func(int& i_):i(i_){} <span style="font-family: Courier;">// 这里是一个引用的i的初始化</span> void operator()() { for(unsigned j=0;j<1000000;++j) { do_something(i); } }};void oops(){ int some_local_state=0; func my_func(some_local_state); std::thread my_thread(my_func); my_thread.detach(); // 使线程脱离,即这里不用等待线程的结束,如果换成my_thread.join()则没问题,因为join会阻塞,知道my_thread线程结束才会返回} //函数执行完毕以后,线程可能依旧还在执行,这时候对some_local_state的访问就是非法的
当心在等待线程结束的时候, 如果在调用join()之前,程序发生异常,则有可能调用不到join()!
常规解决办法是在catch里面join():
struct func;void f(){ int some_local_state=0; func my_func(some_local_state); std::thread t(my_func); try { do_something_in_current_thread(); } catch(...) { t.join();</span> throw; } t.join();}
较优的解决方案是使用RAII,这样代码较简洁,也不容易出错
class thread_guard{ std::thread& t; public: explicit thread_guard(std::thread& t_):t(t_){} ~thread_guard() { if(t.joinable()) //注意 这个判断是必要的 因为一个thread只能被调用一次join(),增加该判断是防止线程在别的地方已经被join()了! { t.join(); } } thread_guard(thread_guard const&)=delete; // c++11的用法 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(); }说明:因为t是局部变量,当程序退出f()的作用域时t会析构,从而调用join()达到等待线程结束的目的。
调用detach()方法可以使线程在后台运行,从而无法再joinable();
std::thread t(do_background_work);t.detach();assert(!t.joinable());
多文档操作是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); } }}参数传递,线程在传递参数的时候是采用拷贝的方式,所以在传参的时候当心悬垂指针
void f(int i,std::string const& s);void oops(int some_param){ char buffer[1024]; sprintf(buffer, "%i",some_param); std::thread t(f,3,buffer); t.detach();}线程在将buffer转化为string&的时候可能oops已经执行完毕退出,从而造成buffer是悬垂指针
解决办法:
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,std::string(buffer)); //显示转换 t.detach();}同时由于线程是使用拷贝的方式传参,所以在传递给线程来更新的数据则要传递引用:
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,data); // 这里对data进行拷贝传值 display_status(); t.join(); process_widget_data(data); //由于是传值 所以这里的数据没有更新 还是原来的值}解决办法是使用std::ref() :
std::thread t(update_data_for_widget,w,std::ref(data)); //传递的是data的引用还可以传递类方法给thread对象:
class X{ public: void do_lengthy_work();}; X my_x; std::thread t(&X::do_lengthy_work,&my_x); // 最后线程调用:my_x.do_lengthy_work()
对于不能进行拷贝的对象,则要使用move方法,例如uniqe_ptr对象,uniqe_ptr不能复制,只能转移即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));同样的std::thread对象也是只能move而不能copy的对象,所以一个线程同时只能和一个thread对象关联,但是却可以在thread对象之间用move传递,当线程被move以后,就成了一个空对象(没有线程与之关联);
void some_function();void some_other_function();std::thread t1(some_function);std::thread t2=std::move(t1); // t2显现在关联<span style="font-family: Courier;">some_function的线程,t1悬空</span>t1=std::thread(some_other_function); // t1关联新创建的线程,注意这里的t1在赋值前是空的std::thread t3; //声明空的线程对象t3t3=std::move(t2); //将t2的线程传递给t3,t2悬空t1=std::move(t3);//将t3的线程传递给t1,注意这里的t1在赋值前不是空线程对象,而是有关联线程,在赋值执行完之后,会导致std::terminate()被调用,从而使整个程序停 止,因为线程只能被join或者detach,而不能被丢弃(t1=std::move(t3)就相当于丢弃原先t1的线程)。thread对象的move特性是它可以作为函数的返回值:
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;}
当将thread作为参数传递的时候自然要使用move方法了:
void f(std::thread t);void g(){ void some_function(); f(std::thread(some_function)); std::thread t(some_function); f(std::move(t));}
scope_thread(注意与前面的RAII的不同),这个类构造的时候是直接持有线程,并不是引用:
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();}可以用vector来管理线程,因为有move特性,vector就直接持有该线程对象
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::for_each(threads.begin(),threads.end(), std::mem_fn(&std::thread::join)); //std::mem_fn 是让循环的对象每个都调用一次某个方法,可以是对象的成员函数,这里是std::thread::join方法}几个有用的std::方法
std::accumulate // 求和
std::distance // 迭代器之间的距离(长度)
std::thread::hardware_concurrency // 当前系统最大线程并行数线程的身份证,std::thread::id,该id支持拷贝和比较,当id相等时可以判断2个线程相同(要么是同一个线程,要么都是没有关联线程的空的std::thread对象),因为该id满足完全的比较特性,如a>b,b>c 则 a>c, 所以可以用来sort排序,并且还支持std::hash<std::thread::id> ,即可以用来hash!
获取id的方法有2种:1 使用thread对象调用方法get_id(),当thread对象没有与线程关联,则该方法返回一个默认构造的id对象,该对象可以用来说明该对象是"not any thread"; 2 是在当前线程调用方法std::this_thread::get_id() 来获取!
示例
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();}该id还可用来输出:
std::cout<<std::this_thread::get_id();同一个id输出的内容是一样的!
0 0
- C++11 并发实战阅读笔记(1)
- C++11 并发实战阅读笔记(2)
- java并发实战阅读笔记 --1
- 《java并发编程实战》笔记(1)
- Linux C 编程实战--阅读笔记
- java并发编程实战阅读笔记(第二章)线程安全性
- java并发编程实战阅读笔记(第三章)对象的共享
- java并发编程实战阅读笔记(第四章)对象的组合
- java并发实战阅读笔记 --2
- 关于《Java并发编程实战》 -- 第一部分的阅读笔记
- 关于《Java并发编程实战》 -- 第二部分的阅读笔记
- Lucene实战阅读笔记1
- 《Java并发编程实战》- 个人阅读总结(一)
- Java并发编程实战笔记(1)- 线程安全
- 《java并发编程实战》笔记(1-2)
- 《Maven实战》阅读笔记整理(一)
- 《Maven实战》阅读笔记整理(二)
- 《Maven实战》阅读笔记整理(三)
- makefile多目录的.c 格式.cpp混合编译
- 视频解码研究之PTS(1)TS格式和FLV格式
- C语言:折半插入排序与二分查找
- 怎样在MindMapper中添加网页链接
- 爬爬爬之路:UI(十六) 数据持久化初级(沙盒实现)
- C++11 并发实战阅读笔记(1)
- android 动画效果
- 安卓事件分发机制一个函数解析
- Git学习总结(3)——代码托管平台简介
- 我爱北大
- Xcode编译Undefined symbols for architecture xxx 错误总结
- 同源策略和跨域访问
- java amr格式转mp3格式(完美解决Linux下转换0K问题)
- Jira管理员配置手册