Ambiorix
28th July 2006, 10:09
Hello,
Qt4 claims to have a thread safe way of emitting signals between threads. Many of Qt4's slots have Qt4 data types in their signature. The copy constructors of these types are reentrant but not thread safe. However this problem is avoided since most of the time const references are used as slot arguments e.g. void mySlot(QString const& arg). That means that the slot gets the address of the data but cannot modify these data. Okay! But what about the thread that emitted the signal. I see trouble when in the emitting thread the QString goes out of scope or is modified before the slot dealt with the QString. What stunned me was that my program nevertheless crashed when I used in the emitting thread a const QString that did not went out of scope!! When I wrote similar code for a slot with an integer argument the program did not crash. My conclusions: 1. Qt4 is doing something dirty when queing a signal to the receivers event loop. 2. Qt4's signal/slot mechanism across threads is NOT THREAD SAFE.
I include my code which I have build using the Visual Studio .NET 2003 compiler on a Windows XP-system.
Can anybody either confirm my observations/conclusions or state whether I did some wrong coding?
Thanks in advance!
// FILE 1: receiver.h
#ifndef __RECEIVER__H__
#define __RECEIVER__H__
#include <QtCore>
class Receiver : public QObject
{
Q_OBJECT
public:
Receiver(QObject *parent = 0) : QObject(parent)
{
}
public slots:
void mySlot(QString const& paS);
void myIntegerSlot(int paI);
};
#endif
// FILE 2 emitter.h
#ifndef __EMITTER__H__
#define __EMITTER__H__
#include <QtCore>
class Receiver;
class Emitter : public QObject
{
Q_OBJECT
public:
Emitter(QObject *parent = 0) : QObject(parent)
{
}
void emitSignal(QString const& paS);
void emitIntegerSignal(int paI);
signals:
void newString(QString const& paS);
void newInteger(int paI);
};
class EmitterThread : public QThread
{
public:
EmitterThread(Receiver *receiver);
void run();
private:
Receiver *meReceiver;
Emitter *meEmitter;
};
#endif
// FILE 3 receiver.cpp
#include "receiver.h"
#include <iostream>
using namespace std;
void Receiver::mySlot(QString const& paS)
{
cout << paS.toStdString() << endl;
}
void Receiver::myIntegerSlot(int paI)
{
cout << paI << endl;
}
// FILE 4: emitter.cpp
#include "emitter.h"
#include "receiver.h"
#include <iostream>
using namespace std;
void Emitter::emitSignal(QString const& paS)
{
emit newString(paS);
}
void Emitter::emitIntegerSignal(int paI)
{
emit newInteger(paI);
}
EmitterThread::EmitterThread(Receiver *receiver) : QThread(0), meReceiver(receiver)
{
meEmitter = new Emitter;
QObject::connect(meEmitter,SIGNAL(newString(QStrin g const&)),
meReceiver,SLOT(mySlot(QString const&)),
Qt::QueuedConnection);
QObject::connect(meEmitter,SIGNAL(newInteger(int)) ,
meReceiver,SLOT(myIntegerSlot(int)),
Qt::QueuedConnection);
}
/* This run method crashes
void EmitterThread::run()
{
QString loText("");
while (true)
{
QThread::sleep(1);
meEmitter->emitSignal(loText);
loText += QString("_");
}
}
*/
/* This run method crashes
void EmitterThread::run()
{
while (true)
{
this->sleep(1);
meEmitter->emitSignal(QString("I am not thread safe"));
}
}
*/
/* This run method crashes
void EmitterThread::run()
{
while (true)
{
QThread::sleep(1);
{
QString loText("I go out of scope while my colleague thread is using me!");
meEmitter->emitSignal(loText);
}
}
}
*/
// This run method crashes
void EmitterThread::run()
{
QString const loText("I go not out of scope and am not modified");
while (true)
{
this->sleep(1);
meEmitter->emitSignal(loText);
}
}
/* This run method does not crash (it emits a copy of an integer)
void EmitterThread::run()
{
int i = 0;
while (true)
{
this->msleep(10);
meEmitter->emitIntegerSignal(i);
i++;
}
}
*/
// FILE 5: main.cpp
#include "receiver.h"
#include "emitter.h"
#include <QtCore>
#include <QtGui>
int main(int argc, char *argv[])
{
QApplication app(argc,argv);
Receiver loReceiver; // lives in Main Thread
EmitterThread loET(&loReceiver);
loET.start();
return app.exec();
}
Qt4 claims to have a thread safe way of emitting signals between threads. Many of Qt4's slots have Qt4 data types in their signature. The copy constructors of these types are reentrant but not thread safe. However this problem is avoided since most of the time const references are used as slot arguments e.g. void mySlot(QString const& arg). That means that the slot gets the address of the data but cannot modify these data. Okay! But what about the thread that emitted the signal. I see trouble when in the emitting thread the QString goes out of scope or is modified before the slot dealt with the QString. What stunned me was that my program nevertheless crashed when I used in the emitting thread a const QString that did not went out of scope!! When I wrote similar code for a slot with an integer argument the program did not crash. My conclusions: 1. Qt4 is doing something dirty when queing a signal to the receivers event loop. 2. Qt4's signal/slot mechanism across threads is NOT THREAD SAFE.
I include my code which I have build using the Visual Studio .NET 2003 compiler on a Windows XP-system.
Can anybody either confirm my observations/conclusions or state whether I did some wrong coding?
Thanks in advance!
// FILE 1: receiver.h
#ifndef __RECEIVER__H__
#define __RECEIVER__H__
#include <QtCore>
class Receiver : public QObject
{
Q_OBJECT
public:
Receiver(QObject *parent = 0) : QObject(parent)
{
}
public slots:
void mySlot(QString const& paS);
void myIntegerSlot(int paI);
};
#endif
// FILE 2 emitter.h
#ifndef __EMITTER__H__
#define __EMITTER__H__
#include <QtCore>
class Receiver;
class Emitter : public QObject
{
Q_OBJECT
public:
Emitter(QObject *parent = 0) : QObject(parent)
{
}
void emitSignal(QString const& paS);
void emitIntegerSignal(int paI);
signals:
void newString(QString const& paS);
void newInteger(int paI);
};
class EmitterThread : public QThread
{
public:
EmitterThread(Receiver *receiver);
void run();
private:
Receiver *meReceiver;
Emitter *meEmitter;
};
#endif
// FILE 3 receiver.cpp
#include "receiver.h"
#include <iostream>
using namespace std;
void Receiver::mySlot(QString const& paS)
{
cout << paS.toStdString() << endl;
}
void Receiver::myIntegerSlot(int paI)
{
cout << paI << endl;
}
// FILE 4: emitter.cpp
#include "emitter.h"
#include "receiver.h"
#include <iostream>
using namespace std;
void Emitter::emitSignal(QString const& paS)
{
emit newString(paS);
}
void Emitter::emitIntegerSignal(int paI)
{
emit newInteger(paI);
}
EmitterThread::EmitterThread(Receiver *receiver) : QThread(0), meReceiver(receiver)
{
meEmitter = new Emitter;
QObject::connect(meEmitter,SIGNAL(newString(QStrin g const&)),
meReceiver,SLOT(mySlot(QString const&)),
Qt::QueuedConnection);
QObject::connect(meEmitter,SIGNAL(newInteger(int)) ,
meReceiver,SLOT(myIntegerSlot(int)),
Qt::QueuedConnection);
}
/* This run method crashes
void EmitterThread::run()
{
QString loText("");
while (true)
{
QThread::sleep(1);
meEmitter->emitSignal(loText);
loText += QString("_");
}
}
*/
/* This run method crashes
void EmitterThread::run()
{
while (true)
{
this->sleep(1);
meEmitter->emitSignal(QString("I am not thread safe"));
}
}
*/
/* This run method crashes
void EmitterThread::run()
{
while (true)
{
QThread::sleep(1);
{
QString loText("I go out of scope while my colleague thread is using me!");
meEmitter->emitSignal(loText);
}
}
}
*/
// This run method crashes
void EmitterThread::run()
{
QString const loText("I go not out of scope and am not modified");
while (true)
{
this->sleep(1);
meEmitter->emitSignal(loText);
}
}
/* This run method does not crash (it emits a copy of an integer)
void EmitterThread::run()
{
int i = 0;
while (true)
{
this->msleep(10);
meEmitter->emitIntegerSignal(i);
i++;
}
}
*/
// FILE 5: main.cpp
#include "receiver.h"
#include "emitter.h"
#include <QtCore>
#include <QtGui>
int main(int argc, char *argv[])
{
QApplication app(argc,argv);
Receiver loReceiver; // lives in Main Thread
EmitterThread loET(&loReceiver);
loET.start();
return app.exec();
}