c++并发编程之thread::join()和thread::detach()
thread::join(): 阻塞当前线程,直至 *this 所标识的线程完成其执行。*this 所标识的线程的完成同步于从 join() 的成功返回。
该方法简单暴力,主线程等待子进程期间什么都不能做。thread::join()会清理子线程相关的内存空间,此后thread object将不再和这个子线程相关了,即thread object不再joinable了,所以join对于一个子线程来说只可以被调用一次,为了实现更精细的线程等待机制,可以使用条件变量等机制。
#include <iostream>
#include <thread>
#include <chrono> void foo()
{
std::cout << "foo is started\n";
// 模拟昂贵操作
std::this_thread::sleep_for(std::chrono::seconds());
std::cout << "foo is done\n";
} void bar()
{
std::cout << "bar is started\n";
// 模拟昂贵操作
std::this_thread::sleep_for(std::chrono::seconds());
std::cout << "bar is done\n";
} int main()
{
std::cout << "starting first helper...\n";
std::thread helper1(foo); std::cout << "starting second helper...\n";
std::thread helper2(bar); std::cout << "waiting for helpers to finish...\n" << std::endl;
helper1.join();
helper2.join(); std::cout << "done!\n";
}
运行结果:
starting first helper...
starting second helper...
foo is started
waiting for helpers to finish...
bar is started foo is done
bar is done
done!
异常环境下join,假设主线程在一个函数f()里面创建thread object,接着f()又调用其它函数g(),那么确保在g()以任何方式下退出主线程都能join子线程。如:若g()通过异常退出,那么f()需要捕捉异常后join.
#include<iostream>
#include<boost/thread.hpp>
void do_something(int& i){
i++;
}
class func{
public:
func(int& i):i_(i){}
void operator() (){
for(int j=;j<;j++)
do_something(i_);
}
public:
int& i_;
};
void do_something_in_current_thread(){}
void f(){
int local=;
func my_func(local);
boost::thread t(my_func);
try{
do_something_in_current_thread();
}
catch(...){
t.join();//确保在异常条件下join子线程
throw;
}
t.join();
}
int main(){
f();
return ;
}
上面的方法看起来笨重,有个解决办法是采用RAII(资源获取即初始化),将一个thread object通过栈对象A管理,在栈对象A析构时调用thread::join.按照局部对象析构是构造的逆序,栈对象A析构完成后再析构thread object。如下:
#include<iostream>
#include<boost/noncopyable.hpp>
#include<boost/thread.hpp>
using namespace std;
class thread_guard:boost::noncopyable{
public:
explicit thread_guard(boost::thread& t):t_(t){}
~thread_guard(){
if(t_.joinable()){//检测是很有必要的,因为thread::join只能调用一次,要防止其它地方意外join了
t_.join();
}
}
//thread_guard(const thread_guard&)=delete;//c++11中这样声明表示禁用copy constructor需要-std=c++0x支持,这里采用boost::noncopyable已经禁止了拷贝和复制
//thread_guard& operator=(const thread_guard&)=delete;
private:
boost::thread& t_;
};
void do_something(int& i){
i++;
}
class func{
public:
func(int& i):i_(i){}
void operator()(){
for(int j=;j<;j++)
do_something(i_);
}
public:
int& i_;
};
void do_something_in_current_thread(){}
void fun(){
int local=;
func my_func(local);
boost::thread t(my_func);
thread_guard g(t);
do_something_in_current_thread();
}
int main(){
fun();
return ;
}
thread::detach(): 从 thread 对象分离执行的线程,允许执行独立地持续。一旦线程退出,则释放所有分配的资源。调用 detach
后, *this 不再占有任何线程。
#include <iostream>
#include <chrono>
#include <thread> void independentThread()
{
std::cout << "Starting concurrent thread.\n";
std::this_thread::sleep_for(std::chrono::seconds());
std::cout << "Exiting concurrent thread.\n";
} void threadCaller()
{
std::cout << "Starting thread caller.\n";
std::thread t(independentThread);
t.detach();
std::this_thread::sleep_for(std::chrono::seconds());
std::cout << "Exiting thread caller.\n";
} int main()
{
threadCaller();
std::this_thread::sleep_for(std::chrono::seconds());
std::cout << "back to main.\n";
}
运行结果:
Starting thread caller.
Starting concurrent thread.
Exiting thread caller.
Exiting concurrent thread.
back to main.
如果注释掉main函数里的std::this_thread::sleep_for(std::chrono::seconds(5)); 即不等待independentThread 执行完。运行结果如下:
Starting thread caller.
Starting concurrent thread.
Exiting thread caller.
back to main.
c++并发编程之thread::join()和thread::detach()的更多相关文章
- 并发编程之Fork/Join
并发与并行 并发:多个进程交替执行. 并行:多个进程同时进行,不存在线程的上下文切换. 并发与并行的目的都是使CPU的利用率达到最大.Fork/Join就是为了尽可能提高硬件的使用率而应运而生的. 计 ...
- 并发编程之fork/join(分而治之)
1.什么是分而治之 分而治之就是将一个大任务层层拆分成一个个的小任务,直到不可拆分,拆分依据定义的阈值划分任务规模. fork/join通过fork将大任务拆分成小任务,在将小任务的结果join汇总 ...
- python并发编程之Queue线程、进程、协程通信(五)
单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...
- python并发编程之threading线程(一)
进程是系统进行资源分配最小单元,线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.进程在执行过程中拥有独立的内存单元,而多个线程共享内存等资源. 系列文章 py ...
- 并发编程之:JMM
并发编程之:JMM 大家好,我是小黑,一个在互联网苟且偷生的农民工. 上一期给大家分享了关于Java中线程相关的一些基础知识.在关于线程终止的例子中,第一个方法讲到要想终止一个线程,可以使用标志位的方 ...
- 并发编程之:Atomic
大家好,我是小黑,一个在互联网苟且偷生的农民工. 在开始讲今天的内容之前,先问一个问题,使用int类型做加减操作是不是线程安全的呢?比如 i++ ,++i,i=i+1这样的操作在并发情况下是否会有问题 ...
- 并发编程之:CountDownLatch
大家好,我是小黑,一个在互联网苟且偷生的农民工. 先问大家一个问题,在主线程中创建多个线程,在这多个线程被启动之后,主线程需要等子线程执行完之后才能接着执行自己的代码,应该怎么实现呢? Thread. ...
- 并发编程之wait()、notify()
前面的并发编程之volatile中我们用程序模拟了一个场景:在main方法中开启两个线程,其中一个线程t1往list里循环添加元素,另一个线程t2监听list中的size,当size等于5时,t2线程 ...
- 并发编程之 Exchanger 源码分析
前言 JUC 包中除了 CountDownLatch, CyclicBarrier, Semaphore, 还有一个重要的工具,只不过相对而言使用的不多,什么呢? Exchange -- 交换器.用于 ...
- 并发编程之 Condition 源码分析
前言 Condition 是 Lock 的伴侣,至于如何使用,我们之前也写了一些文章来说,例如 使用 ReentrantLock 和 Condition 实现一个阻塞队列,并发编程之 Java 三把锁 ...
随机推荐
- 外部事件/中断的区别及EXTI->SWIER的用途
EXTI_SWIER作用:允许我们通过程序控制就可以启动中断/事件线 1.产生事件的线路最终的产物是一个脉冲信号,这个脉冲信号可以给其他外设电路使用,比如定时器TIM.模拟数字转换器ADC等等. 2. ...
- Nuxt.js + koa2 入门
1. nuxt项目初始化 下面是使用 koa 模板方法初始化一个项目,使用该方法需要将 nuxt 的版本降至1.4.2: 官方 https://zh.nuxtjs.org/guide/installa ...
- SpringBoot日记——MQ消息队列整合(二)
基于第一篇文章搭建好环境以后,我们这篇文章继续介绍如何在springboot中使用RabbitMQ. 1).单播:添加好pom文件和自定义配置后,来看: @Autowired RabbitTempla ...
- Oracle实用地址
1.详细安装教程 https://jingyan.baidu.com/article/3c48dd34be2a32e10be35881.html
- 通用shellcode
所有 win_32 程序都会加载 ntdll.dll 和 kernel32.dll 这两个最基础的动态链接库.如果想要 在 win_32 平台下定位 kernel32.dll 中的 API 地址,可以 ...
- torchvision 批量可视化图片
1.1 简介 计算机视觉中,我们需要观察我们的神经网络输出是否合理.因此就需要进行可视化的操作. orchvision是独立于pytorch的关于图像操作的一些方便工具库. torchvision的详 ...
- 微软职位内部推荐-Senior Software Development Engineer_Commerce
微软近期Open的职位: Are you looking for a high impact project that involves processing of billions of dolla ...
- 【Alpha】第三次Scrum meeting
今日任务一览: 导航栏诞生 前期准备的Latex文本将撰写完毕 生成燃尽图的问题已经解决 姓名 今日完成任务 所耗时间 刘乾 用Github成功生成了燃尽图(真是不容易啊...),与架构师继续每日面基 ...
- 【Alpha】第九次Scrum meeting
今日任务一览: 姓名 今日完成任务 所耗时间 刘乾 由于发现之前版本的模板引擎存在致命bug,所以重新更换,现在使用jinja2作为模板渲染引擎. 3 鲁聃 卤蛋今天为了给编译老师做视频,没有做软工项 ...
- 《Linux内核设计与实现》第一二章读书笔记
第一章 Linux内核简介 1.Unix简介 (一)概念:支持抢占式多任务.多进程.虚拟内存.换页.动态链接和TCP/IP网络的现代化操作系统. (二)Unix特点(层次化结构): Unix很简洁,仅 ...