从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();
}
这样将会得到一个