Qt signal argument stream safety

Suppose I have a signal sendImage(const QImage&)

that is connected to a slot updateLabel(const QImage&)

in another thread that converts a QImage to a QPixmap and then puts it in a QLabel. Now I am wondering if I am using a function const QImage& prepareImage()

as a signal argument, eg. emit sendImage(prepareImage())

and the signal is emitted dozens of times per second, is it thread safe or is there a possibility that the rank condition happens between prepareImage and updateLabel while simultaneously accessing the image, thus crashing the program?

+3


source to share


3 answers


Luckily, Qt shields you from itself and copies the image so you don't get shot in the foot. The copy will be done when the signal is allocated and is done from within the signal implementation - here the copy is done while Object::source

on the call stack.

Given that a is QImage

implicitly split, the original copy will be cheap, but if the main thread then changes the original image, it forces you to make a deep copy. If your modifications are such that the original code is discarded, it will be more efficient to replace the original image with a new one rather than "modify" it.

Output:



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"

      

+6


source


It depends on the connection between SIGNAL and SLOT.

If you use the default, which is Qt::AutoConnection

, it acts as Qt::QueuedConnection

for multi-thread connections.

In Queued Connection, all signal parameters are copied to the queue and passed by value, even if you pass them by reference.



Therefore, there is no possibility that the state of race can occur.

Note. QImage implements CopyOnWrite (Implicit Sharing), which means if you manage to change the QImage's internal buffer in some way, your sync will explode.

+2


source


First of all, I don't quite understand what you mean by this:

is there any chance the rank condition occurs between prepareImage and updateLabel while accessing the image at the same time.

One thread has created an object and (let's say it's the same object for a moment), another is using it. Where exactly is the moment when both threads are using it at the same time?

Even if it does, in your case, the QImage created on the first thread will be copied and passed to another, so it's not the same object.

0


source







All Articles