假设我有一个信号sendImage(const QImage&)
连接到另一个线程中的插槽updateLabel(const QImage&)
,它将QImage转换为QPixmap,然后将其放入QLabel。现在我想知道,如果我使用函数const QImage& prepareImage()
作为信号的参数,例如emit sendImage(prepareImage())
,信号每秒发出几十次,它是线程安全的还是有可能在prepareImage和updateLabel之间发生竞争条件同时访问图像而导致程序崩溃?
答案 0 :(得分:6)
值得庆幸的是,Qt会保护您自己,并会复制图像,以免您在脚下射击。复制将在信号发射时完成,并从信号实现的内部完成 - 这里,复制在Object::source
在调用堆栈上时完成。
鉴于隐式共享QImage
,初始副本将很便宜,但如果主线程随后修改源图像,则会强制执行深层复制。如果您的修改是废弃的源,那么用新的替换源图像而不是"修改"会更有效。它
输出:
data is at 0x7fff5fbffbf8 in main thread QThread(0x10250a700)
0x7fff5fbffbf8 was copied to 0x1025115d0 in thread QThread(0x10250a700)
got 0x1025115d0 in thread QThread(0x7fff5fbffb80)
#include <QCoreApplication>
#include <QDebug>
#include <QThread>
class Copyable {
public:
Copyable() {}
Copyable(const Copyable & src) {
qDebug() << static_cast<const void*>(&src) << "was copied to"
<< static_cast<void*>(this) << "in thread" << QThread::currentThread();
}
};
Q_DECLARE_METATYPE(Copyable)
class Object : public QObject {
Q_OBJECT
public:
Q_SIGNAL void source(const Copyable &);
Q_SLOT void sink(const Copyable & data) {
qDebug() << "got" << static_cast<const void*>(&data) << "in thread"
<< QThread::currentThread();
// Queue a quit since we are racing with app.exec(). qApp->quit() is a no-op before
// the app.exec() has had a chance to block.
QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
}
};
class Thread : public QThread { public: ~Thread() { quit(); wait(); } };
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Copyable data;
qDebug() << "data is at" << static_cast<void*>(&data) << "in main thread" << app.thread();
qRegisterMetaType<Copyable>();
Object o1, o2;
Thread thread;
o2.moveToThread(&thread);
thread.start();
o2.connect(&o1, &Object::source, &o2, &Object::sink);
emit o1.source(data);
return app.exec();
}
#include "main.moc"
答案 1 :(得分:2)
这取决于SIGNAL和SLOT之间的连接。
如果您使用的是默认值Qt::AutoConnection
,则它就像Qt::QueuedConnection
一样用于跨线程连接。
在“排队连接”中,即使您通过引用传递它们,所有信号参数也会复制到队列并按值传递。
因此,不可能出现竞争条件。
注意:QImage实现了CopyOnWrite(隐式共享),这意味着如果您设法以某种方式更改QImage的内部缓冲区,您的同步将会爆炸。
答案 2 :(得分:0)
首先,我不太明白你的意思是:
是否有可能在prepareImage和updateLabel之间发生竞争条件,同时访问图像
一个线程创建了一个对象(让我们假设这是同一个对象)另一个正在使用它。两个线程同时使用它的时刻究竟在哪里?
即使它发生了,在你的情况下,在第一个线程中创建的QImage将被复制并传递给另一个,所以这不是同一个对象。