C++11 – 使用std::thread::join()/std::thread::detach()方法需要注意的点
本文作者:StubbornHuang
版权声明:本文为站长原创文章,如果转载请注明原文链接!
原文标题:C++11 – 使用std::thread::join()/std::thread::detach()方法需要注意的点
原文链接:https://www.stubbornhuang.com/1165/
发布于:2021年02月26日 14:58:29
修改于:2021年02月26日 14:59:21

1 调用std::thread::join()方法等待线程退出时的示例问题程序
#include <iostream>
#include "conio.h"
#include <memory>
#include <thread>
#include <atomic>
class BaseThread
{
public:
typedef std::shared_ptr<BaseThread> ptr;
BaseThread()
{
m_Age = 10;
m_ChildThreadPtr = nullptr;
m_bChildStop = false;
}
virtual~BaseThread()
{
std::cout << "析构" << std::endl;
}
void ChildThreadFunc()
{
while (!m_bChildStop)
{
m_Age++;
std::cout << "子线程运行" << std::endl;
std::this_thread::sleep_for(std::chrono::microseconds(3000000));
}
}
void StartThread()
{
if (m_ChildThreadPtr != nullptr)
m_ChildThreadPtr.reset();
m_ChildThreadPtr = std::make_shared<std::thread>(&BaseThread::ChildThreadFunc, this);
}
void StopThread()
{
m_bChildStop = true;
if (m_ChildThreadPtr != nullptr)
{
m_ChildThreadPtr->join();
//if (m_ChildThreadPtr->joinable())
//{
// m_ChildThreadPtr->join();
//}
}
}
private:
std::atomic<int> m_Age;
std::shared_ptr<std::thread> m_ChildThreadPtr;
std::atomic<bool> m_bChildStop;
};
int main()
{
std::shared_ptr<BaseThread> pBaseThread = std::make_shared<BaseThread>();
if (pBaseThread != nullptr)
{
pBaseThread->StartThread();
while (true)
{
// 在此处填入需要循环的代码
if (_kbhit()) // 如果有按键被按下
{
if (_getch() == 'q') //如果按下了q键则跳出循环
{
std::cout << "退出" << std::endl;
// 不小心执行了两次停止线程
pBaseThread->StopThread();
}
}
}
}
getchar();
return 0;
}
在上述程序中,我们声明了一个BaseThread类,在该类中我们使用类的成员函数ChildThreadFunc()作为子线程函数,并使用StartThread()开启执行子线程,StopThread()停止执行子线程。
在main()函数中我们先使用StartThread()开启执行子线程,然后再通过按q键调用StopThread()退出子线程。
但是我们一不小心就连续按了两下q键,导致调用了两次StopThread()方法,这个时候程序出现了crash,并出现了如下报错:

2 问题原因
在上述main函数中,当我们按下了第一次q键的时候,子程序已经调用了join方法,这导致std::thread对象失去了与之相关联的线程对象,所以当我们再按下了一次q键,发现现在的std::thread对象已经不可join,导致了程序发生了中断。
3 std::thread出现不可join的几种情况
std::thread在以下几种情况下是不可join的:
- 由std::thread默认构造的std::thread对象,也就是没有指定线程函数的std::thread对象是不可join的;
- 该std::thread对象被std::move操作
- 该std::thread对象已经执行过std::thread::join()方法或者std::thread::detach()方法,此时std::thread对象是不可join的;
4 使用std::thread::join()/std::thread::detach()方法需要注意的点
4.1 在执行std::thread::join()/std::thread::detach()方法之前最好判断该std::thread对象是可join的
if (m_ChildThreadPtr->joinable())
{
m_ChildThreadPtr->join();
}
即第1节中的bug可以如此修复:
#include <iostream>
#include "conio.h"
#include <memory>
#include <thread>
#include <atomic>
class BaseThread
{
public:
typedef std::shared_ptr<BaseThread> ptr;
BaseThread()
{
m_Age = 10;
m_ChildThreadPtr = nullptr;
m_bChildStop = false;
}
virtual~BaseThread()
{
std::cout << "析构" << std::endl;
}
void ChildThreadFunc()
{
while (!m_bChildStop)
{
m_Age++;
std::cout << "子线程运行" << std::endl;
std::this_thread::sleep_for(std::chrono::microseconds(3000000));
}
}
void StartThread()
{
if (m_ChildThreadPtr != nullptr)
m_ChildThreadPtr.reset();
m_ChildThreadPtr = std::make_shared<std::thread>(&BaseThread::ChildThreadFunc, this);
}
void StopThread()
{
m_bChildStop = true;
if (m_ChildThreadPtr != nullptr)
{
if (m_ChildThreadPtr->joinable())
{
m_ChildThreadPtr->join();
}
}
}
private:
std::atomic<int> m_Age;
std::shared_ptr<std::thread> m_ChildThreadPtr;
std::atomic<bool> m_bChildStop;
};
int main()
{
std::shared_ptr<BaseThread> pBaseThread = std::make_shared<BaseThread>();
if (pBaseThread != nullptr)
{
pBaseThread->StartThread();
while (true)
{
// 在此处填入需要循环的代码
if (_kbhit()) // 如果有按键被按下
{
if (_getch() == 'q') //如果按下了q键则跳出循环
{
std::cout << "退出" << std::endl;
pBaseThread->StopThread();
}
}
}
}
getchar();
return 0;
}
在调用join之前判断joinable,只有在线程对象是可join的情况下再进行join或者detach操作。
4.2 不要忘记对有关联的std::thread对象调用join或者detach方法
这里有关联的指的是已经为该线程指定了线程函数。
例如我们将第一节的程序进行修改,删除按q键退出子程序的代码。
#include <iostream>
#include "conio.h"
#include <memory>
#include <thread>
#include <atomic>
class BaseThread
{
public:
typedef std::shared_ptr<BaseThread> ptr;
BaseThread()
{
m_Age = 10;
m_ChildThreadPtr = nullptr;
m_bChildStop = false;
}
virtual~BaseThread()
{
std::cout << "析构" << std::endl;
}
void ChildThreadFunc()
{
while (!m_bChildStop)
{
m_Age++;
std::cout << "子线程运行" << std::endl;
std::this_thread::sleep_for(std::chrono::microseconds(3000000));
}
}
void StartThread()
{
if (m_ChildThreadPtr != nullptr)
m_ChildThreadPtr.reset();
m_ChildThreadPtr = std::make_shared<std::thread>(&BaseThread::ChildThreadFunc, this);
}
void StopThread()
{
m_bChildStop = true;
if (m_ChildThreadPtr != nullptr)
{
if (m_ChildThreadPtr->joinable())
{
m_ChildThreadPtr->join();
}
}
}
private:
std::atomic<int> m_Age;
std::shared_ptr<std::thread> m_ChildThreadPtr;
std::atomic<bool> m_bChildStop;
};
int main()
{
std::shared_ptr<BaseThread> pBaseThread = std::make_shared<BaseThread>();
if (pBaseThread != nullptr)
{
pBaseThread->StartThread();
}
getchar();
return 0;
}
运行上述程序,程序报错:
在一个有关联的线程函数的std::thread对象如果没有调用join或者detach方法会在std::thread对象析构的时候中断程序。如果程序发生异常,也必须在异常处理中调用std::thread对象的join或者detach方法。
当前分类随机文章推荐
- C++11 - override关键字简要介绍 阅读1459次,点赞0次
- C++ - 在Windows/Linux上创建单级目录以及多级目录的跨平台方法 阅读205次,点赞0次
- C++ - 常用的C++命令行参数解析第三方库 阅读209次,点赞0次
- C++ - 字节数组byte[]或者unsigned char[]与long的相互转换 阅读292次,点赞0次
- C++ - Windows/Linux生成uuid(通用唯一识别码) 阅读351次,点赞1次
- C++ - std::map正向遍历与反向遍历的几种方式 阅读2446次,点赞3次
- C++ - 我的代码风格备忘 阅读124次,点赞0次
- C++ - 函数返回多个返回值的方法总结 阅读708次,点赞0次
- C++11/std::shared_ptr - 循环引用问题 阅读2928次,点赞0次
- C++ 11 - final关键字简要介绍 阅读1366次,点赞0次
全站随机文章推荐
- TensorRT - 安装TensorRT工具Polygraphy 阅读1697次,点赞0次
- 资源分享 - Handbook of Computer Aided Geometric Design 英文高清PDF下载 阅读1069次,点赞0次
- WordPress - get_footer函数,加载主题底部页脚footer模板 阅读299次,点赞0次
- 资源分享 - Designing the User Experience of Game Development Tools 英文高清PDF下载 阅读319次,点赞0次
- 资源分享 - Polygon Mesh Processing英文高清PDF下载 阅读5158次,点赞1次
- 工具网站推荐 - HDR高动态范围图像下载地址 阅读1702次,点赞0次
- 资源分享 - Vector Analysis for Computer Graphics , First Edition 英文高清PDF下载 阅读505次,点赞0次
- 资源分享 - 精通Python网络爬虫 核心技术、框架与项目实战 ,韦玮著 高清PDF下载 阅读1324次,点赞0次
- 简单粗暴:使用pycharm安装对应的Python版本第三方包 阅读2896次,点赞0次
- Duilib - CDuiString转换为std::string 阅读1168次,点赞0次
评论
149