thread::join(): 阻塞当前线程,直至 *this 所标识的线程完成其执行。*this 所标识的线程的完成同步于从 join() 的成功返回。

该方法简单暴力,主线程等待子进程期间什么都不能做。thread::join()会清理子线程相关的内存空间,此后thread object将不再和这个子线程相关了,即thread object不再joinable了,所以join对于一个子线程来说只可以被调用一次,为了实现更精细的线程等待机制,可以使用条件变量等机制。

  1. #include <iostream>
  2. #include <thread>
  3. #include <chrono>
  4.  
  5. void foo()
  6. {
  7. std::cout << "foo is started\n";
  8. // 模拟昂贵操作
  9. std::this_thread::sleep_for(std::chrono::seconds());
  10. std::cout << "foo is done\n";
  11. }
  12.  
  13. void bar()
  14. {
  15. std::cout << "bar is started\n";
  16. // 模拟昂贵操作
  17. std::this_thread::sleep_for(std::chrono::seconds());
  18. std::cout << "bar is done\n";
  19. }
  20.  
  21. int main()
  22. {
  23. std::cout << "starting first helper...\n";
  24. std::thread helper1(foo);
  25.  
  26. std::cout << "starting second helper...\n";
  27. std::thread helper2(bar);
  28.  
  29. std::cout << "waiting for helpers to finish...\n" << std::endl;
  30. helper1.join();
  31. helper2.join();
  32.  
  33. std::cout << "done!\n";
  34. }

运行结果:

  1. starting first helper...
  2. starting second helper...
  3. foo is started
  4. waiting for helpers to finish...
  5. bar is started
  6.  
  7. foo is done
  8. bar is done
  9. done!

异常环境下join,假设主线程在一个函数f()里面创建thread object,接着f()又调用其它函数g(),那么确保在g()以任何方式下退出主线程都能join子线程。如:若g()通过异常退出,那么f()需要捕捉异常后join.

  1. #include<iostream>
  2. #include<boost/thread.hpp>
  3. void do_something(int& i){
  4. i++;
  5. }
  6. class func{
  7. public:
  8. func(int& i):i_(i){}
  9. void operator() (){
  10. for(int j=;j<;j++)
  11. do_something(i_);
  12. }
  13. public:
  14. int& i_;
  15. };
  16. void do_something_in_current_thread(){}
  17. void f(){
  18. int local=;
  19. func my_func(local);
  20. boost::thread t(my_func);
  21. try{
  22. do_something_in_current_thread();
  23. }
  24. catch(...){
  25. t.join();//确保在异常条件下join子线程
  26. throw;
  27. }
  28. t.join();
  29. }
  30. int main(){
  31. f();
  32. return ;
  33. }

上面的方法看起来笨重,有个解决办法是采用RAII(资源获取即初始化),将一个thread object通过栈对象A管理,在栈对象A析构时调用thread::join.按照局部对象析构是构造的逆序,栈对象A析构完成后再析构thread object。如下:

  1. #include<iostream>
  2. #include<boost/noncopyable.hpp>
  3. #include<boost/thread.hpp>
  4. using namespace std;
  5. class thread_guard:boost::noncopyable{
  6. public:
  7. explicit thread_guard(boost::thread& t):t_(t){}
  8. ~thread_guard(){
  9. if(t_.joinable()){//检测是很有必要的,因为thread::join只能调用一次,要防止其它地方意外join了
  10. t_.join();
  11. }
  12. }
  13. //thread_guard(const thread_guard&)=delete;//c++11中这样声明表示禁用copy constructor需要-std=c++0x支持,这里采用boost::noncopyable已经禁止了拷贝和复制
  14. //thread_guard& operator=(const thread_guard&)=delete;
  15. private:
  16. boost::thread& t_;
  17. };
  18. void do_something(int& i){
  19. i++;
  20. }
  21. class func{
  22. public:
  23. func(int& i):i_(i){}
  24. void operator()(){
  25. for(int j=;j<;j++)
  26. do_something(i_);
  27. }
  28. public:
  29. int& i_;
  30. };
  31. void do_something_in_current_thread(){}
  32. void fun(){
  33. int local=;
  34. func my_func(local);
  35. boost::thread t(my_func);
  36. thread_guard g(t);
  37. do_something_in_current_thread();
  38. }
  39. int main(){
  40. fun();
  41. return ;
  42. }

thread::detach(): 从 thread 对象分离执行的线程,允许执行独立地持续。一旦线程退出,则释放所有分配的资源。调用 detach 后, *this 不再占有任何线程。

  1. #include <iostream>
  2. #include <chrono>
  3. #include <thread>
  4.  
  5. void independentThread()
  6. {
  7. std::cout << "Starting concurrent thread.\n";
  8. std::this_thread::sleep_for(std::chrono::seconds());
  9. std::cout << "Exiting concurrent thread.\n";
  10. }
  11.  
  12. void threadCaller()
  13. {
  14. std::cout << "Starting thread caller.\n";
  15. std::thread t(independentThread);
  16. t.detach();
  17. std::this_thread::sleep_for(std::chrono::seconds());
  18. std::cout << "Exiting thread caller.\n";
  19. }
  20.  
  21. int main()
  22. {
  23. threadCaller();
  24. std::this_thread::sleep_for(std::chrono::seconds());
    std::cout << "back to main.\n";
    }

运行结果:

  1. Starting thread caller.
  2. Starting concurrent thread.
  3. Exiting thread caller.
  4. Exiting concurrent thread.
    back to main.

如果注释掉main函数里的std::this_thread::sleep_for(std::chrono::seconds(5)); 即不等待independentThread 执行完。运行结果如下:

  1. Starting thread caller.
  2. Starting concurrent thread.
  3. Exiting thread caller.
  4. back to main.

c++并发编程之thread::join()和thread::detach()的更多相关文章

  1. 并发编程之Fork/Join

    并发与并行 并发:多个进程交替执行. 并行:多个进程同时进行,不存在线程的上下文切换. 并发与并行的目的都是使CPU的利用率达到最大.Fork/Join就是为了尽可能提高硬件的使用率而应运而生的. 计 ...

  2. 并发编程之fork/join(分而治之)

    1.什么是分而治之 分而治之就是将一个大任务层层拆分成一个个的小任务,直到不可拆分,拆分依据定义的阈值划分任务规模. fork/join通过fork将大任务拆分成小任务,在将小任务的结果join汇总 ...

  3. python并发编程之Queue线程、进程、协程通信(五)

    单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...

  4. python并发编程之threading线程(一)

    进程是系统进行资源分配最小单元,线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.进程在执行过程中拥有独立的内存单元,而多个线程共享内存等资源. 系列文章 py ...

  5. 并发编程之:JMM

    并发编程之:JMM 大家好,我是小黑,一个在互联网苟且偷生的农民工. 上一期给大家分享了关于Java中线程相关的一些基础知识.在关于线程终止的例子中,第一个方法讲到要想终止一个线程,可以使用标志位的方 ...

  6. 并发编程之:Atomic

    大家好,我是小黑,一个在互联网苟且偷生的农民工. 在开始讲今天的内容之前,先问一个问题,使用int类型做加减操作是不是线程安全的呢?比如 i++ ,++i,i=i+1这样的操作在并发情况下是否会有问题 ...

  7. 并发编程之:CountDownLatch

    大家好,我是小黑,一个在互联网苟且偷生的农民工. 先问大家一个问题,在主线程中创建多个线程,在这多个线程被启动之后,主线程需要等子线程执行完之后才能接着执行自己的代码,应该怎么实现呢? Thread. ...

  8. 并发编程之wait()、notify()

    前面的并发编程之volatile中我们用程序模拟了一个场景:在main方法中开启两个线程,其中一个线程t1往list里循环添加元素,另一个线程t2监听list中的size,当size等于5时,t2线程 ...

  9. 并发编程之 Exchanger 源码分析

    前言 JUC 包中除了 CountDownLatch, CyclicBarrier, Semaphore, 还有一个重要的工具,只不过相对而言使用的不多,什么呢? Exchange -- 交换器.用于 ...

  10. 并发编程之 Condition 源码分析

    前言 Condition 是 Lock 的伴侣,至于如何使用,我们之前也写了一些文章来说,例如 使用 ReentrantLock 和 Condition 实现一个阻塞队列,并发编程之 Java 三把锁 ...

随机推荐

  1. 20155229《网络对抗技术》Exp6:信息收集与漏洞扫描

    实验内容 (1)各种搜索技巧的应用 (2)DNS IP注册信息的查询 (3)基本的扫描技术:主机发现.端口扫描.OS及服务版本探测.具体服务的查点 (4)漏洞扫描:会扫,会看报告,会查漏洞说明,会修补 ...

  2. 20155235 《网络攻防》 实验九 Web安全基础

    20155235 <网络攻防> 实验九 Web安全基础 实验内容 SQL注入攻击 XSS攻击 CSRF攻击 WebGoat WebGoat是OWASP组织研制出的用于进行web漏洞实验的应 ...

  3. Metasploit简单应用

    什么是Metasploit Metasploit是一款开源的安全漏洞检测工具. 它可以帮助用户识别安全问题,验证漏洞的缓解措施,并对某些软件进行安全性评估,提供真正的安全风险情报.当我们第一次接触Me ...

  4. [c#]记一次实验室局域网的ARP欺骗

    起因 某天中午午睡时,笔者被激烈的键盘和鼠标声音吵醒,发现实验室的同学在那边忘我地打LOL,顿觉不爽,于是决定整他一下.想了一下之后觉得就让他掉线一下作为惩罚好了.结合以往的理论知识,大家在同一个局域 ...

  5. 几种flash存储芯片的用途和分类

    1.IIC EEPROM------容量小,采用的是IIC通信协议:用于在掉电时,存系统配置参数,比如屏幕亮度等.常用芯片型号有 AT24C02.FM24C02.CAT24C02等,其常见的封装多为D ...

  6. python 算法面试题

    1.题目是:有一组“+”和“-”符号,要求将“+”排到左边,“-”排到右边,写出具体的实现方法. def StringSort(data): startIndex=0 endIndex=0 count ...

  7. R语言学习 第二篇:矩阵和数组

    向量是一维的,只有行这一个维度,没有其他维度.R可以创建更高维度的数据对象,例如,矩阵.数据框.数组,索引高维度的对象时,需要使用元素的下标.这些对象的下标都使用中括号[]和索引,第一个维度是row, ...

  8. 基于.NET的3D开发框架/工具比较

    好久木有写博客了,这么日子以来忙忙碌碌的,也没大有时间潜心学习和梳理.最近刚好接手一个3D显示的活,虽然还没怎么弄明白,但是也看过一些方案,走了一些弯路,经过一些浅显的了解(3D Display这里面 ...

  9. numpy 初识(二)

    针对 numpy.array(序列)的实例介绍 ndim 数组(矩阵)的维度 size 所有元素的和 数学运算(+, -) 元素个数一样,对应位置相减 加,减,乘,平方一个数,执行广播形式:即都减去一 ...

  10. Apache Ignite 学习笔记(四): Ignite缓存冗余备份策略

    Ignite的数据网格是围绕着基于内存的分布式key/value存储能力打造的.当初技术选型的时候,决定用Ignite也是因为虽然同样是key/value存储,它有着和其他key/value存储系统不 ...