从C++11开始,C++STL开始支持线程,
先看一个C++11 thread的例子
#include "stdafx.h"
#include <thread>
#include <chrono>
#include <iostream>
#include <functional>
#include <string>
bool isRunning = true;
std::thread* tptr;
uint8_t* byte_ptr;
constexpr size_t kLen = 10;
static void ShowMem(uint8_t* p, const size_t size = kLen)
{
for (size_t i = 0; i < kLen; ++i)
{
printf("%02x", p[i]);
}
printf("\n");
}
void thread_func()
{
static int i = 0;
while (isRunning)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "thread_func finished" << std::endl;
}
void Shell_Thread()
{
std::string input;
while (1)
{
std::cin >> input;
if (input == "exit")
{
exit(EXIT_SUCCESS);
}
if (input == "showmem")
{
ShowMem(byte_ptr, sizeof(std::thread));
continue;
}
if (input == "release")
{
delete tptr;
continue;
}
}
}
int main()
{
do {
std::thread t(Shell_Thread); t.detach();
} while (0);
tptr = new std::thread(thread_func);
tptr->detach();
byte_ptr = reinterpret_cast<uint8_t*>(tptr);
while (1)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
在windows平台编译成debug版本,然后运行
先查看任务管理器
线程数是3
main函数一个主线程,Shell_Thread,thread_func 分别一个线程,总共3个线程
在控制台输入release 回车,然后输入showmem回车
release
showmem
dddddddddddddddddddd
可以看到输出dddd...
window平台,debug模式下free掉的内存会被系统填充成0xdd
销毁了线程并且windows也没有报错。
在任务管理器下面可以看到
tptr 所指向的thread对象销毁了,但thread_func 这个函数仍然在系统中运行。
现在将if (input == "release")
分支下delete tptr;
这一句换成isRunning = false;
重复上面执行一下可以看到
showmem
0000000000000000fdfd
release
thread_func finished
showmem
0000000000000000fdfd
线程数变成了2,但是tptr指向的内存没被free,内存泄露了。
在std::cout << "thread_func finished" << std::endl;
这个语句后面加上一句delete tptr
可以解决问题,但明显不会是一个好的方式。
需要自行包装一下Thread,在线程运行结束后自我销毁,或者由第三个对象在判断线程运行结束后再销毁线程对象。
现在看Qt下面一个多线程的例子
#include <QtCore/QCoreApplication>
#include <QThread>
#include <QDebug>
#include <iostream>
class MyWrap
{
public:
explicit MyWrap();
virtual ~MyWrap();
bool isRunning;
QThread* t;
};
class MyThread : public QThread {
public:
explicit MyThread(MyWrap*);
void run() Q_DECL_OVERRIDE;
MyWrap* m_wrap;
};
class MyShell : public QThread {
public:
void run() Q_DECL_OVERRIDE;
};
MyWrap *wrap_ptr;
QThread *tptr;
uint8_t* byte_ptr;
constexpr size_t kLen = 10;
static void ShowMem(uint8_t* p, const size_t size = kLen)
{
QString str;
for (size_t i = 0; i < kLen; ++i)
{
str += QString::number(p[i], 16);
}
qDebug() << str;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
do {
QThread *shell = new MyShell();
shell->start();
} while (0);
wrap_ptr = new MyWrap();
tptr = wrap_ptr->t;
byte_ptr = reinterpret_cast<uint8_t*>(tptr);
tptr->start();
return a.exec();
}
void MyShell::run()
{
std::string input;
while (1)
{
std::cin >> input;
if (input == "exit")
{
QCoreApplication::exit(0);
return;
}
if (input == "showmem")
{
ShowMem(byte_ptr, sizeof(MyThread));
continue;
}
if (input == "release")
{
delete wrap_ptr;
continue;
}
}
}
MyThread::MyThread(MyWrap* wrap)
:QThread(),m_wrap(wrap)
{
}
void MyThread::run()
{
while (m_wrap->isRunning)
{
QThread::sleep(1);
}
qDebug() << __FUNCTION__ << " end";
this->deleteLater();
}
MyWrap::MyWrap()
: isRunning(true)
{
this->t = new MyThread(this);
this->t->start();
}
MyWrap::~MyWrap()
{
isRunning = false;
}
在上面例子中把m_wrap->isRunning
换成一个全局的变量,程序可以按照预期的方向运行。
将MyWrap与MyThread修改成如下
MyWrap::~MyWrap()
{
isRunning = false;
this->t->quit();
this->t->wait();
delete this->t;
}
void MyThread::run()
{
while (m_wrap->isRunning)
{
QThread::sleep(1);
}
qDebug() << __FUNCTION__ << " end";
}
程序也会如期运行。
但MyThread::run中执行太多耗时操作,而MyWrap是界面的操作,将会导致界面长时间卡顿。
将MyWrap与MyThread修改成如下
MyWrap::~MyWrap()
{
this->t->requestInterruption();
}
void MyThread::run()
{
while (!this->isInterruptionRequested())
{
if (IsBadReadPtr(m_wrap,sizeof(MyWrap))) { break; }
// read m_wrap-> ....
QThread::sleep(1);
}
qDebug() << __FUNCTION__ << " end";
this->deleteLater();
}
这样将会优雅的结束线程并销毁线程。
千万不要像下面这样
MyWrap::~MyWrap()
{
this->t->requestInterruption();
this->t->deleteLater();
}
这样将会得到一个