C++11 – 使用std::thread::join()/std::thread::detach()方法需要注意的点
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方法。
本文作者:StubbornHuang
版权声明:本文为站长原创文章,如果转载请注明原文链接!
原文标题:C++11 – 使用std::thread::join()/std::thread::detach()方法需要注意的点
原文链接:https://www.stubbornhuang.com/1165/
发布于:2021年02月26日 14:58:29
修改于:2023年06月26日 21:51:49
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
评论
52