我发现 Qt 和 QLabel 存在内存泄漏问题。
当启动 QThread 并从中发出信号以更新 GUI 并更改信号中 QLabel 的文本时,可以注意到(使用 Windows 任务管理器)RAM 快速增加。
测试此代码的代码非常简单,是从应用程序中提取的,在该应用程序中,我从同一线程并使用相同的信号更改不同项目的文本,唯一导致问题的是 QLabel。
如果我不是从线程更改文本,而是从主窗口中的 while 循环更改文本,则不会出现问题。
我强调,我从同一线程更改多个对象的文本,没有任何内存泄漏,并且应用程序保持稳定。下班后使用 QLabel,应用程序达到 2GB 并且变得难以管理。
我在主窗口用户界面中添加了一个启动线程的按钮和标签。全局 g_MainFormUnit 是对表单的引用。我在我的代码中使用了这种技术,这不会在其他地方造成任何问题。
该代码是使用 Qt 6.4.2 MinGW 64 位调试针对 Windows 编译的。
这是我的代码。
主窗口.cpp
#include <QThread>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "spydriver_thread.h"
#include "shared.h"
Ui::MainWindow * g_MainFormUnit;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
g_MainFormUnit = ui;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_StartThreadButton_clicked()
{
// Capture Thread to fill packets
SpyDriver_ShowViewThread * workerThread = new SpyDriver_ShowViewThread ();
workerThread->start();
QObject::connect(workerThread, &SpyDriver_ShowViewThread::InsertPacket, workerThread, &SpyDriver_ShowViewThread::on_InsertPacket);
}
spydriver_thread.cpp
#include <QtCore>
// Includes
#include <stdio.h>
#include <string.h>
// Qt Includes
#include <QtGlobal>
#include <QThread>
#include <QCoreApplication>
#include <QtConcurrent/QtConcurrent>
#include "spydriver_thread.h"
#include "shared.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
void SpyDriver_ShowViewThread::on_InsertPacket (int in_Packet)
{
char PacketCounterText [16];
memset (PacketCounterText, 0, sizeof(PacketCounterText));
sprintf (PacketCounterText, "[%05d]", in_Packet);
g_MainFormUnit->SessionPacketNumberLabel->setText(QString(PacketCounterText)); // **This causes the memory leak**
return;
}
// --- CONSTRUCTOR ---
SpyDriver_ShowViewThread::SpyDriver_ShowViewThread()
{
return;
}
// --- DECONSTRUCTOR ---
SpyDriver_ShowViewThread::~SpyDriver_ShowViewThread()
{
// free resources
return;
}
主窗口.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_StartThreadButton_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
spydriver_thread.h
#ifndef __MULTISERIAL_SPYDRIVER_THREAD_H__
#define __MULTISERIAL_SPYDRIVER_THREAD_H__
#include <QtCore>
// Qt Includes
#include <QtGlobal>
#include <QThread>
#include <QCoreApplication>
#include <QtConcurrent/QtConcurrent>
#include <stdio.h>
class SpyDriver_ShowViewThread : public QThread
{
Q_OBJECT
void run() override {
int i = 0;
while (1)
{
emit InsertPacket (i);
i++;
}
}
public:
SpyDriver_ShowViewThread();
~SpyDriver_ShowViewThread();
public slots:
void on_InsertPacket (int in_Packet);
signals:
void InsertPacket (int in_Packet);
private:
};
#endif
共享.h
#ifndef SHARED_H
#define SHARED_H
#include "mainwindow.h"
extern Ui::MainWindow * g_MainFormUnit;
#endif // SHARED_H
您的线程发出信号的速度比 GUI 线程处理信号的速度快。他们排队,并且队列大小不断增长。
确实有道理,GUI 线程也在做很多其他事情,例如更新屏幕和处理操作系统事件。您的工作线程 OTOH 除了添加为 GUI 线程事件循环发出的排队信号外什么也没做。
向工作线程添加一个微小的睡眠,或者在信号发出和接收槽之间添加一个信号量,以限制排队信号的数量。
一般的解决方案是添加一个单独的信号处理步骤,要么在发出线程中,要么在另一个线程中(或者可能在 GUI 线程中)。中间步骤应跟踪 UI 应显示的值,但仅以较长的时间间隔更新 UI。对于任何非实时、非游戏类应用程序来说,100 毫秒的间隔可能就足够了。