在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文章来源:https://www.toymoban.com/news/detail-617617.html
文章来源地址https://www.toymoban.com/news/detail-617617.html
到了这里,关于Qt中postevent造成内存泄漏问题的通用解决方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!