c++并发编程(一)---基本线程管理
来源:互联网 发布:二战美国驱逐舰数据 编辑:程序博客网 时间:2024/06/06 01:27
1. 启动线程
线程是通过构造std::thread对象开始的,对该对象指定了线程上要运行的任务,在最简单的情况下,该任务仅仅是一个普普通通的返回void而且不接受参数的函数,这个函数在自己的线程上运行,直到返回,然后线程停止。但从另一个极端看,该任务可能是一个接收额外参数的函数对象,放它运行时,会执行一系列由某种消息机制所指定的相互独立的操作,并且只有当线程再次通过某种消息机制接收到信号才会停止。无论线程将要做什么或是从哪里启动,都需要构造std::thread对象。
#include<thread> void do_some_work() std::thread my_thread(do_some_work);
也可以将一个带有函数调用操作符的实例(如仿函数)传递给std::thread的构造函数来进行替代。
class background_task{ public: void operator() ()const { do_something(); do_something_else(); }};background_task f;std::thread my_thread(f);
还可以使用lambda表达式。
std::thread my_thread([](){ do_something(); });
一旦线程启动,你需要显式的决定等待完成还是让它自行运行,如果你在std::thread对象被销毁前还未做决定,那么你的程序会被终止(std::thread的析构函数会调用std::terminate())。因此在异常存在的情况下,确保线程正确地结合或者分离是当务之急。如果你不等待线程完成,那么你需要确保通过该线程访问的数据是有效的,直到线程完成为止。在对象销毁后还访问它是未定义行为。
如:
struct func{ int& i; func(int& i_):i(i_){} void operator() () { for(unsigned int 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(); //不等待线程完成 //新的线程仍在运行}
在这种情况下,当oops退出时线程还在运行,但是存储在栈中的局部变量随着函数的退出被释放,此时线程中仍在访问该变量,所以会发生未定义行为。此时可在代码中加入my_thread.join()解决。使用join()需要注意一个问题,为了避免应用程序在引发异常的时候被终止,需要注意在存在异常的时候调用join(),以避免意外的生命周期问题。
struct fun; //详见上图代码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(); //异常中断条件下 throw; } t.join(); //正常退出条件下}
使用try/catch块很啰嗦,而且很容易将作用域弄luan,这里可以使用资源获取即初始化(RAII)惯用方法,并提供一个类,在它的析构函数中进行join()。
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;//将拷贝构造函数和拷贝运算符标记为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();}
当代码执行到f末尾时,局部对象会按照构造函数的逆序被销毁。因此,thread_guard对象g首先被销毁,并且析构函数中t.join()被结合,即使函数因为do_something_in_current_thread引发异常而退出的情况下也会发生。如果无需等待线程完成,可以通过分离来避免这个异常安全问题。这打破了线程与std::thread对象联系并确保当std::thread对象被销毁时std::terminate()不会被调用,即使线程仍在后台运行。
2. 传递参数给线程函数
传递参数给可调用对象或函数,基本上就是简单的将额外的参数传递给std::thread的构造函数,但重要的是参数会以默认的方式被复制到内部的存储空间,在那里新创建的执行线程可以访问它们,即便函数中相应参数期待着引用。
void f(int i,std::string const & s);std::thread t(f,3,"hello");
这里创建一个新的与t相关联的执行线程,称为f(3,”hello”)。注意即使f接收std::string作为第二个参数时,字符串字面值仅在新线程的上下文中才作为char const*传送,并转换为std::string。尤其重要的是当提供的参数是一个自动变量的指针时。
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();}
如果oops在buffer在转化成std::string之前退出会发生未定义行为,因此解决之道是直接在std::thread的构造函数中传入std::string(buffer);
3.转移线程所有权。
c++标准库有许多拥有资源的类型,如std::ifstream和std::unique_ptr,都是可移动的而非可复制的,而且std::thread也是其中之一,都将拷贝构造函数和拷贝运算符写入私有,或者声明为delete。一个特定执行的线程所有权可以在std::thread之间移动。如下例所示,该实例创建了两个执行线程,以及三个std::thread实例t1,t2和t3之间对那些线程的所有权进行转移。
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); //⑥
首先启动一个新线程①与t1关联,然后t2构建完成时所有权被转移给t2,通过调用std::move()来显式地转移所有权②。此刻t1不再拥有相关联的执行线程,运行some_function的线程现在与t2相关联。
然后,启动一个新的线程并与一个临时的std::thread对象相关联③,接下来将所以权转移到t1中,是不需要调用std::move()来显式转移所有权的,因为此处所有者是一个临时对象-从临时对象中进行移动是自动的和隐式的。
t3是默认构造的④,这意味着没有默认相关联的线程,当前t2转移给t3,t1与some_other_function()相关联,t2没有关联的线程,t3与运行的some_function()关联。
最后一次移动⑥将运行some_function()的线程的所以权转回给t1,但t1已经有一个相关联的线程,所以会调用std::terminate()来终止程序。
- c++并发编程(一)---基本线程管理
- Java并发编程之线程管理(基本线程同步2)
- Java并发编程之线程管理(基本线程同步3)
- Java并发编程之线程管理(基本线程同步4)
- Java并发编程之线程管理(基本线程同步5)
- Java并发编程之线程管理(基本线程同步6)
- Java 并发编程(三)线程管理
- java并发编程实战手册(一)线程管理
- java并发编程(一)线程安全性
- java并发编程(一)-线程池
- java并发编程(一)----线程基础知识
- Java并发编程之线程(一)
- 并发编程--线程池Executor(一)
- Java并发编程之线程管理(线程创建1)
- c++并发探索1-线程基本管理
- 并发编程(4)线程的生命周期及基本状态
- Java并发编程之线程管理(Executor框架11)
- Java并发编程之线程管理(Executor框架12)
- 【计算机组成原理】第4章 指令系统
- node爬虫之转码
- oracle存储过程实例
- [Java] JavaBean浅析
- php使用websocket示例详解
- c++并发编程(一)---基本线程管理
- Core Technologies
- Linux中的关机与启动
- 用于从后台返回值的多选的选中
- IOS基础控件 Layer swift3.1
- 学习使用greys排查线上问题
- 虚拟主机常见免费问题解答!
- 差分约束算法总结
- Mybatis和Hibernate的比较