//Case 1:
QImage* tImg = new QImage("Some Image Here");
painter->drawImage(x, y, *tImg );
...
delete tImg;
//Case 2:
QImage* tImg = new QImage("Some Image Here");
{
QImage aImg(*tImg);
painter->drawImage(x, y, aImg );
}
...
delete tImg;
我正在尝试在工作线程中加载多个图像并在主线程中绘制它们。但是我不确定在绘制它们之后删除工作线程中的图像是否可以。
//Case 3:
...
//In worker thread
QImage* tImg = new QImage("Some Image Here");
mutex.lock();
matrix.insert(tImg); // matrix is a QList
mutex.unlock();
...
//In main thread
mutex.lock();
foreach(QImage* tImg, matrix)
{
painter->drawImage(x, y, *tImg);
}
mutex.unlock();
...
//In worker thread
mutex.lock();
matrix.remove(tImg);
delete tImg;
mutex.unlock();
以上代码会导致问题吗?由于drawImage函数是"传递const引用"。这会导致任何内存问题吗?
如果delete tImg
在另一个帖子上怎么办?如果我使用互斥锁来确保delete tImg
仅在painter->drawImage(x, y, *tImg );
之后调用
答案 0 :(得分:3)
不需要手动内存管理。你应该利用Qt为你做这件事。您可以通过信号槽连接传递图像,并使用该值将被自动复制的事实,并且任何对它的访问都将由Qt自动同步。
以下是如何做到这一点,非常简单,让编译器为您完成资源管理的所有艰苦工作:
// https://github.com/KubaO/stackoverflown/tree/master/questions/imageloader-36265788
#include <QtWidgets>
#include <QtConcurrent>
首先,让我们有一个带有信号的类,它是图像源。它有一个提供const引用类型图像的信号,因为如果需要跨越线程边界,任何复制都将由Qt自动完成。
class ImageSource : public QObject {
Q_OBJECT
public:
Q_SIGNAL void hasImage(const QImage & image);
方法生成图像,并发出信号。只要自动连接与hasImage
信号一起使用,此方法就可以在任何线程中安全运行。在我们的例子中,我们总是从工作线程运行此方法,但我们也可以从主线程运行它 - 唯一的区别在于性能。
/// This method is thread-safe (ignoring the UB of incrementing a shared int)
void generate() {
static auto counter = 0;
QImage img(128, 128, QImage::Format_ARGB32);
img.fill(Qt::white);
QPainter p(&img);
p.drawText(img.rect(), Qt::AlignCenter, QString::number(counter++));
p.end();
emit hasImage(img);
}
};
我们需要该类的一个实例,以及显示图像的内容 - 比如QLabel
:
int main(int argc, char ** argv) {
QApplication app{argc, argv};
ImageSource source;
QLabel label;
label.show();
我们现在可以将hasImage
连接到一个设置标签大小并在其上设置图像的仿函数。然后它立即在全局池的工作线程中再次运行图像生成器。这由QtConcurrent::run
自动处理。
仿函数在主线程中运行:通过向connect
:connect(--, --, context, --)
提供上下文参数来确保这一点。仿函数在label.thread()
中运行,正如我们希望的那样。
QObject::connect(&source, &ImageSource::hasImage, &label, [&](const QImage & image){
label.setFixedSize(image.size());
label.setPixmap(QPixmap::fromImage(image));
QtConcurrent::run(&source, &ImageSource::generate);
});
由于连接是自动的,调用hasImage
信号的效果会导致将槽调用发布到接收对象(label
)线程的事件队列 - 这里是主线程的队列。事件循环获取插槽调用并执行它。因此,即使在工作线程中调用hasImage
,图像也会自动复制并传递给主线程中的仿函数。
最后,我们生成第一个图像以启动该过程。
QtConcurrent::run(&source, &ImageSource::generate); // generate the first image
return app.exec();
}
最后需要#include
来提供信号hasImage
信号的实现,以及描述ImageSource
类的元数据。它由moc生成。
#include "main.moc"
这是完整的代码,您可以将其粘贴到新项目中,编译并运行;或者从github链接下载完整的项目。
它显示了一个标签,可以在我的机器上以大约1000 / s的速率更新像素图。应用程序完全响应:您可以随意移动窗口,并随时退出。
有关线程图像加载器的另一个示例,请参阅this answer。