原始C++标准仅支持单线程编程。新的C++标准(称为C++11或C++0x)于2011年发布。在C++11中,引入了新的线程库。因此运行本文程序需要C++至少符合C++11标准。

2 连接和分离线程

在本章中,我们将讨论std::thread的连接和分离。

2.1 用std::thread::join()连接线程

一旦启动一个线程,则另一个线程可以等待该新线程完成。为此,还需要在std::thread对象上调用join()函数,即

  1. std::thread th(funcPtr);
  2. // Some Code
  3. th.join();

让我们看一个例子,假设主线程必须启动10个工作线程,并且在启动所有这些线程之后,主函数将等待它们完成。连接所有线程后,主函数将继续。

  1. #include <iostream>
  2. #include <thread>
  3. #include <algorithm>
  4. #include <vector>
  5. class WorkerThread
  6. {
  7. public:
  8. void operator()()
  9. {
  10. std::cout << "Worker Thread " << std::this_thread::get_id() << " is Executing" << std::endl;
  11. }
  12. };
  13. int main()
  14. {
  15. // 创建多个线程
  16. std::vector<std::thread> threadList;
  17. for (int i = 0; i < 10; i++)
  18. {
  19. threadList.push_back(std::thread(WorkerThread()));
  20. }
  21. // Now wait for all the worker thread to finish i.e.
  22. // Call join() function on each of the std::thread object
  23. // 现在等待所有工作线程完成,即对每个std::thread对象调用join()函数
  24. std::cout << "wait for all the worker thread to finish" << std::endl;
  25. std::for_each(threadList.begin(), threadList.end(), std::mem_fn(&std::thread::join));
  26. std::cout << "Exiting from Main Thread" << std::endl;
  27. return 0;
  28. }

输出为:

  1. Worker Thread 12616 is ExecutingWorker Thread 10584 is Executing
  2. Worker Thread Worker Thread 14696Worker Thread Worker Thread 15356Worker Thread 11228 is Executing
  3. Worker Thread is Executing is Executing
  4. 9528 is Executing
  5. Worker Thread
  6. wait for all the worker thread to finish
  7. Worker Thread 16312 is Executing
  8. 14448 is Executing77361908 is Executing
  9. is Executing
  10. Exiting from Main Thread

2.2 使用std::thread::detach()分离线程

分离的线程也称为守护进程/后台线程。要分离线程,我们需要对std::thread对象调用std::detach()函数,即:

  1. std::thread th(funcPtr);
  2. th.detach();

调用detach()之后,std::thread对象不再与实际的执行线程关联。
注意在线程句柄上调用detach()和join()时要小心!!

情况1:永远不要在没有关联执行线程的std::thread对象上调用join()或detach()

  1. std::thread threadObj( (WorkerThread()) );
  2. threadObj.join();
  3. // 这将导致程序终止 It will cause Program to Terminate
  4. threadObj.join();

当在线程对象上调用join()函数时,则当此join返回0时,则std::thread对象没有与其关联的线程。如果再次在该对象上调用join()函数,则将导致终止程序。同样,调用detach()可以使std::thread对象不与任何线程函数链接。在那种情况下,在std::thread对象上调用detach函数两次将导致程序终止。

  1. std::thread threadObj( (WorkerThread()) );
  2. threadObj.detach();
  3. // 这将导致程序终止 It will cause Program to Terminate
  4. threadObj.detach();

因此,在调用join()或detach()之前,我们应该检查每次线程是否可连接,即

  1. std::thread threadObj( (WorkerThread()) );
  2. if(threadObj.joinable())
  3. {
  4. std::cout<<"Detaching Thread "<<std::endl;
  5. threadObj.detach();
  6. }
  7. if(threadObj.joinable())
  8. {
  9. std::cout<<"Detaching Thread "<<std::endl;
  10. threadObj.detach();
  11. }
  12. std::thread threadObj2( (WorkerThread()) );
  13. if(threadObj2.joinable())
  14. {
  15. std::cout<<"Joining Thread "<<std::endl;
  16. threadObj2.join();
  17. }
  18. if(threadObj2.joinable())
  19. {
  20. std::cout<<"Joining Thread "<<std::endl;
  21. threadObj2.join();
  22. }

情况2:不要忘记在具有关联的执行线程的std::Thread对象上调用Join或Detach
如果具有关联的执行线程的std::Thread对象没有调用Join或Detach,则在该对象的析构期间-否则它将终止程序。因为在析构内部-或者它检查线程是否仍然是可联接的,然后终止程序,即

  1. #include <iostream>
  2. #include <thread>
  3. #include <algorithm>
  4. class WorkerThread
  5. {
  6. public:
  7. void operator()()
  8. {
  9. std::cout << "Worker Thread " << std::endl;
  10. }
  11. };
  12. int main()
  13. {
  14. std::thread threadObj((WorkerThread()));
  15. // Program will terminate as we have't called either join or detach with the std::thread object.
  16. // Hence std::thread's object destructor will terminate the program
  17. // 程序将终止,因为我们没有使用std::Thread对象调用Join或Detach。因此std::Thread的对象析构函数将终止程序
  18. return 0;
  19. }

输出为:

  1. Worker Thread
  2. 程序崩溃

同样,在发生异常的情况下,我们不应忘记调用join()或detach()。为了防止这种情况,我们应该使用“资源获取初始化”( RESOURCE ACQUISITION IS INITIALIZATION,RAII),即

  1. #include <thread>
  2. #include <iostream>
  3. class ThreadRAII
  4. {
  5. std::thread & m_thread;
  6. public:
  7. ThreadRAII(std::thread & threadObj) : m_thread(threadObj)
  8. {
  9. }
  10. ~ThreadRAII()
  11. {
  12. // Check if thread is joinable then detach the thread
  13. // 检查线程是否连接,然后分离线程
  14. if (m_thread.joinable())
  15. {
  16. m_thread.detach();
  17. }
  18. }
  19. };
  20. void thread_function()
  21. {
  22. for (int i = 0; i < 10000; i++);
  23. std::cout << "thread_function Executing" << std::endl;
  24. }
  25. int main()
  26. {
  27. std::thread threadObj(thread_function);
  28. // If we comment this Line, then program will crash
  29. // 如果我们注释此行,则程序将崩溃
  30. ThreadRAII wrapperObj(threadObj);
  31. return 0;
  32. }

输出为:

  1. thread_function Executing

2.3 参考

https://thispointer.com//c11-multithreading-part-3-carefully-pass-arguments-to-threads/

[编程基础] C++多线程入门2-连接和分离线程的更多相关文章

  1. [编程基础] C++多线程入门6-事件处理的需求

    原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 6 事件处 ...

  2. [编程基础] C++多线程入门7-条件变量介绍

    原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 7 条件变 ...

  3. [编程基础] C++多线程入门4-数据共享和资源竞争

    原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++ 11标准. 4 数据共享和资源 ...

  4. [编程基础] C++多线程入门5-使用互斥锁解决资源竞争

    原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 5 使用互 ...

  5. [编程基础] C++多线程入门8-从线程返回值

    原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 8 从线程返回值 8 ...

  6. [编程基础] C++多线程入门1-创建线程的三种不同方式

    原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 1 创建线程的三种不 ...

  7. [编程基础] C++多线程入门10-packaged_task示例

    原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 10 pa ...

  8. [编程基础] C++多线程入门9-async教程和示例

    原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 9 asy ...

  9. [编程基础] C++多线程入门3-小心地将参数传递给线程

    原始C++标准仅支持单线程编程.新的C++标准(称为c++11或c++0x)于2011年发布.在c++11中,引入了新的线程库.因此运行本文程序需要C++至少符合c++11标准. 文章目录 3 小心地 ...

随机推荐

  1. day49-JDBC和连接池05

    JDBC和连接池05 11.BasicDAO 先来分析一个问题 前面我们使用了Apache-DBUtils和Druid简化了JDBC开发,但仍存在以下不足: SQL语句是固定的,不能通过参数传入,通用 ...

  2. 方法的重写(override / overwrite)

    1.重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作 2.应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法. 重写的规 ...

  3. String类型变量的使用

    1.String属于引用数据类型,翻译为:字符串 2.声明String类型变量时,使用一对"" 3.String可以和8种基本数据类型变量做运算,且运算只能是连接运算:+ 4.运算 ...

  4. python不确定性计算之模糊动态聚类实验

    模糊动态聚类实验 本实验所采用的模糊聚类分析方法是基于模糊关系上的模糊聚类法,也称为系统聚类分析法,可分为三步: 第一步:数据标准化,建立模糊矩阵 第二步:建立模糊相似矩阵 第三步:聚类 本程序读取E ...

  5. 洛P8109题解

    摘自本人洛谷博客,原文章地址:https://www.luogu.com.cn/blog/cjtb666anran/solution-p8109 本题原题目摘录: 本场比赛共有 \(n\) 道题,Ci ...

  6. javascript异步编程之generator(生成器函数)与asnyc/await语法糖

    Generator 异步方案 相比于传统回调函数的方式处理异步调用,Promise最大的优势就是可以链式调用解决回调嵌套的问题.但是这样写依然会有大量的回调函数,虽然他们之间没有嵌套,但是还是没有达到 ...

  7. ysoserial CommonsCollections2 分析

    在最后一步的实现上,cc2和cc3一样,最终都是通过TemplatesImpl恶意字节码文件动态加载方式实现反序列化. 已知的TemplatesImpl->newTransformer()是最终 ...

  8. javax.script.ScriptException: Cannot find engine named: 'nashorn', ensure you set language field in JSR223 Test Element: JSR223 预处理程序

    jmeter运行脚本报错,跟java版本有关,做个记录. 1. 问题记录: 执行登录接口测试,登录失败.点击jmeter右上角[黄色!],查看错误日志.显示如下: 2022-09-23 10:29:5 ...

  9. OS-HACKNOS-2.1靶机之解析

    靶机名称 HACKNOS: OS-HACKNOS 靶机下载地址 https://download.vulnhub.com/hacknos/Os-hackNos-1.ova 实验环境 : kali 2. ...

  10. 1.docker的基本使用

    1.简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows操作系统的机器上,也可以实现虚拟化.容器是完 ...