Qt信号参数线程安全

时间:2015-06-29 14:37:36

标签: c++ multithreading qt signals-slots

假设我有一个信号sendImage(const QImage&)连接到另一个线程中的插槽updateLabel(const QImage&),它将QImage转换为QPixmap,然后将其放入QLabel。现在我想知道,如果我使用函数const QImage& prepareImage()作为信号的参数,例如emit sendImage(prepareImage()),信号每秒发出几十次,它是线程安全的还是有可能在prepareImage和updateLabel之间发生竞争条件同时访问图像而导致程序崩溃?

3 个答案:

答案 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将被复制并传递给另一个,所以这不是同一个对象。