c++ - 程序并行 QThread 在应用程序退出时创建内存泄漏
问题描述
我有一个更大的项目,有一个 GUI,我想在后台管理一些文件。我已经为此任务实现了一个新线程,并且在运行时,一切都很好。但是一旦我退出应用程序,就会visual-leak-detector
发现 3-7 个内存泄漏。
我分离了我的线程代码并创建了一个新项目以使用最小的代码示例进行检查,但我仍然无法解决我的问题。
我认为这与主程序的事件循环有关。也许循环没有处理最后一个事件来删除我的线程类和线程本身。因为我在析构函数中停止并退出线程。但我不确定这一点。
这是我的最小代码:threadclass.hpp:
#include <QObject>
#include <QDebug>
class ThreadClass : public QObject {
Q_OBJECT
public:
explicit ThreadClass() {}
virtual ~ThreadClass(){
qDebug() << "ThreadClass Destructor";
}
signals:
// emit finished for event loop
void finished();
public slots:
// scan and index all files in lib folder
void scanAll(){
for(long i = 0; i < 10000; i++){
for (long k = 0; k < 1000000; k++);
if(i%500 == 0)
qDebug() << "thread: " << i;
}
}
// finish event loop and terminate
void stop(){
// will be processed after scanall is finished
qDebug() << "STOP SIGNAL --> EMIT FINSIHED";
emit finished();
}
};
线程处理程序.hpp:
#include <QObject>
#include <QThread>
#include "threadclass.hpp"
class ThreadHandler : public QObject {
Q_OBJECT
public:
explicit ThreadHandler(QObject *parent = 0) : parent(parent), my_thread(Q_NULLPTR) {}
virtual ~ThreadHandler() {
// TODO Check!
// I think I don't have to delete the threads, because delete later
// on finish signal. Maybe I just have to wait, but then how do I
// check, if thread is not finished? Do I need to make a bool var again?
if (my_thread != Q_NULLPTR && my_thread->isRunning())
{
emit stopThread();
//my_thread->quit();
my_thread->wait();
//delete my_thread;
}
qDebug() << "ThreadHandler Destructor";
my_thread->dumpObjectInfo();
}
void startThread(){
if (my_thread == Q_NULLPTR)
{
my_thread = new QThread;
ThreadClass *my_threaded_class = new ThreadClass();
my_threaded_class->moveToThread(my_thread);
// start and finish
QObject::connect(my_thread, &QThread::started, my_threaded_class, &ThreadClass::scanAll);
QObject::connect(this, &ThreadHandler::stopThread, my_threaded_class, &ThreadClass::stop);
// finish cascade
// https://stackoverflow.com/a/21597042/6411540
QObject::connect(my_threaded_class, &ThreadClass::finished, my_threaded_class, &ThreadClass::deleteLater);
QObject::connect(my_threaded_class, &ThreadClass::destroyed, my_thread, &QThread::quit);
QObject::connect(my_thread, &QThread::finished, my_thread, &QThread::deleteLater);
my_thread->start();
}
}
signals:
void stopThread();
private:
QObject *parent;
QThread* my_thread;
};
main.cpp 很糟糕,但似乎很好地模拟了我的主程序的行为:
#include <QCoreApplication>
#include <QTime>
#include <QDebug>
#include "threadhandler.hpp"
#include <vld.h>
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
ThreadHandler *th = new ThreadHandler();
th->startThread();
// wait (main gui programm)
QTime dieTime= QTime::currentTime().addSecs(5);
while (QTime::currentTime() < dieTime) {
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
qDebug() << "DELETE TH";
delete th;
qDebug() << "FINISH ALL EVENTS";
QCoreApplication::processEvents(QEventLoop::AllEvents, 500);
qDebug() << "QUIT";
QCoreApplication::quit();
qDebug() << "CLOSE APP";
// pause console
getchar();
// return a.exec();
}
这是 VLD 的输出:
WARNING: Visual Leak Detector detected memory leaks!
...
turns out this is very boring and uninteresting
...
Visual Leak Detector detected 3 memory leaks (228 bytes).
Largest number used: 608 bytes.
Total allocations: 608 bytes.
Visual Leak Detector is now exiting.
The program '[8708] SOMinimalExampleThreads.exe' has exited with code 0 (0x0).
更新1:我添加qDebug() << "ThreadClass Destructor";
到析构函数中ThreadClass
,我的新输出如下所示:
...
thread: 9996
thread: 9997
thread: 9998
thread: 9999
ThreadHandler Destructor
FINISH ALL EVENTS
CLOSE APP
现在很明显,我的线程类的析构函数从未被调用,因此丢失在 void 中。但是为什么这不起作用呢?
QObject::connect(my_threaded_class, &ThreadClass::finished, my_threaded_class, &ThreadClass::deleteLater);
更新 2:我在 ThreadHandler 中发现了一个问题:
emit stopThread();
my_thread->quit(); // <-- stops the event loop and therefore no deletelater
my_thread->wait();
我删除my_thread->quit()
了,现在调用了 ThreadClass 的析构函数,但my_thread->wait()
从未完成。
解决方案
我发现该my_thread->wait()
函数阻止了事件循环,因此退出和deleteLater
级联从未完成。我用另一种等待完成线程的方法解决了这个问题:
removed
这是 Mike 新实施的解决方案。这很容易实现,我只需要my_threaded_class::stop
在threadhandler
课堂上更改连接即可。
#include <QObject>
#include <QThread>
#include "threadclass.hpp"
class ThreadHandler : public QObject {
Q_OBJECT
public:
explicit ThreadHandler(QObject *parent = 0) : parent(parent), my_thread(Q_NULLPTR) {}
virtual ~ThreadHandler() {
if (my_thread != Q_NULLPTR && my_thread->isRunning())
{
my_thread->quit();
my_thread->wait();
}
qDebug() << "ThreadHandler Destructor";
}
void startThread(){
if (my_thread == Q_NULLPTR)
{
my_thread = new QThread;
ThreadClass *my_threaded_class = new ThreadClass();
my_threaded_class->moveToThread(my_thread);
// start and finish
QObject::connect(my_thread, &QThread::started, my_threaded_class, &ThreadClass::scanAll);
// https://stackoverflow.com/questions/53468408
QObject::connect(my_thread, &QThread::finished, my_threaded_class, &ThreadClass::stop);
// finish cascade
// https://stackoverflow.com/a/21597042/6411540
QObject::connect(my_threaded_class, &ThreadClass::finished, my_threaded_class, &ThreadClass::deleteLater);
QObject::connect(my_threaded_class, &ThreadClass::destroyed, my_thread, &QThread::quit);
QObject::connect(my_thread, &QThread::finished, my_thread, &QThread::deleteLater);
my_thread->start();
}
}
signals:
void stopThread();
private:
QObject *parent;
QThread* my_thread;
};
推荐阅读
- javascript - Polyfill 在 IE11 上不起作用,显示 SCRIPT1003 Expected ":"
- haskell - 使数据类型成为 Functor 的实例以映射到参数类型的字段
- vega-lite - 如何在 Vega-Lite 中创建散点矩阵,其中行/列由值标识(不是列名)
- python-3.x - 使用 beautifulsoup 从 XML 中提取标签内容
- operating-system - 多核调度
- c# - ASP.NET Web API 2 - 注册时的用户电子邮件确认
- azure - 将 Azure DataFactory 管道与计费报告相关联
- c++ - 如果我们不创建新节点并使用指针插入数据并建立链接(在链表中)怎么办?
- python - 计算块的 Merkle 根的代码
- python - 诱变剂:将 ID3 标记保存到变量