Qt中postevent造成内存泄漏问题的通用解决方案

在Qt项目中,使用postevent在后台线程更新图像到主线程的Widget时,导致内存泄漏。问题在于自定义QEvent类未正确析构,内存未被释放。解决方案是使用智能指针包裹QEvent,并在自定义事件类中定义析构函数,确保内存得到释放。此外,为了防止崩溃,还需在Widget类中预分配内存进行数据拷贝。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Qt中由QCoreApplication统一管理Qt事件的收发和销毁,其中sendEvent为阻塞式发送,用于单线程的事件发送;postevent为非阻塞式发送,构造事件的线程和接受事件的线程可以为两个线程。

最近在做一个个人项目ShaderLab

需要绘制OpenGL实时渲染的图像,由于OpenGL渲染基本都放在循环语句内,直接放在主线程会导致界面卡死不响应,所以考虑另开一个线程在后台渲染,再把渲染好的图像在循环语句内通过postevent发送给前端的Widget

 因此要想QCoreApplication注册一个QEvent类型,通过该类型的成员变量保存Image数据

//EvSendFrame.h

#include <QEvent>

class EvSendFrame : public QEvent
{
public: 
	EvSendFrame(void* frameData,int size);
	~EvSendFrame();
	static Type eventType;

public:
	uchar* _framedata;
};

#endif
//EvSendFrame.cpp

#include "EvSendFrame.h"

QEvent::Type EvSendFrame::eventType = (QEvent::Type)QEvent::registerEventType(QEvent::User + 1);

EvSendFrame::EvSendFrame(void* frameData,int size):QEvent(Type(eventType))
{
	_framedata = (uchar*)malloc(size);
	memcpy(_framedata, frameData, size);
}

在OpenGL绘制循环内每更新一次window缓冲区就发送给前端Widget一张图片

	while (tmp = !paused.load(memory_order_acquire))
	{
		auto time = static_cast<float>(glfwGetTime());
		deltaTime = time - lastTime;
		lastTime = time;
		glUniform1f(glGetUniformLocation(_shader->ID, "runtime_data.iTime"), time);
		renderQuad();
		
		switch (_type)
		{
			case XA_GL_RGB:
				glReadPixels(0, 0, SCR_WIDTH, SCR_HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, _windowbuf);
				break;
			case XA_GL_RGBA:
				glReadPixels(0, 0, SCR_WIDTH, SCR_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, _windowbuf);
				break;
			default:
				break;
		}
		flip(&((uint8_t*)_windowbuf));

		auto event = new EvSendFrame(_windowbuf,windowBufSize);
		QApplication::postEvent(_reciver, event, Qt::HighEventPriority);

		glfwSwapBuffers(_window);
		glfwSwapInterval(1);
		glfwPollEvents();
	}

在前端Widget中重载EventFilter虚函数以处理该Event

bool GLWidget::eventFilter(QObject* obj, QEvent* event)
{
	if (event->type() == EvSendFrame::eventType)
	{
		EvSendFrame* ev = (EvSendFrame*)event;
		_picture = QImage(event->_framedata, SCR_WIDTH, SCR_HEIGHT, QImage::Format_RGB888);
		this->repaint();
	}

	return QWidget::eventFilter(obj, event);
}

结果是内存泄漏非常严重,QEvent不会自动释放,以一秒60帧来算,粗略估计就是一秒造成60张600x800图片大小的内存泄漏,非常恐怖!

 可以看到程序的使用内存很快从900M干到了1700多M!对于一个应用程序来说显然是不可接受的。

知道了大概是哪个地方出现内存泄漏后,就可以开始着手修改代码了。按照Stackflow老哥的说法应该将QEvent用智能指针包裹。

auto event = std::make_unique<EvSendFrame>(_windowbuf, windowBufSize);
QApplication::postEvent(_reciver, event.release(), Qt::HighEventPriority);

修改event的构造后发现还是和前面的情况一样,问题出现在哪里?哦,没有定义自定义Event的析构函数

EvSendFrame::EvSendFrame(void* frameData,int size):QEvent(Type(eventType))
{
	_framedata = (uchar*)malloc(size);
	memcpy(_framedata, frameData, size);
}

EvSendFrame::~EvSendFrame()
{
	free(_framedata);
}

这里就要注意一点了,如果你自定义的QEvent类在构造的时候从堆内申请内存,一定要定义该Event的析构函数释放从堆内申请的内存!!!

添加析构函数后运行会崩溃?在前端Widget类中预分配一块同样大小的内存,每次Event销毁前先拷贝需要缓存的数据。

static GLWidget::_glwgt_pctbuffing = (uchar*)malloc(SCR_WIDTH * SCR_HEIGHT * 3 * sizeof(uchar));
static GLWidget::_glwgt_buffingsize = SCR_WIDTH * SCR_HEIGHT * 3 * sizeof(uchar);


bool GLWidget::eventFilter(QObject* obj, QEvent* event)
{
	if (event->type() == EvSendFrame::eventType)
	{
		EvSendFrame* ev = (EvSendFrame*)event;
        memcpy(_glwgt_pctbuffing,ev->_framedata, GLWidget::_glwgt_buffingsize);//copy to unique memory to avoid memory leak
		_picture = QImage(event->_framedata, SCR_WIDTH, SCR_HEIGHT, QImage::Format_RGB888);
		this->repaint();
	}

	return QWidget::eventFilter(obj, event);
}

可以看到在free了_framedata后程序的内存使用情况非常稳定,只有50几M

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值