Is it safe to emit a signal from another thread than the QObject?
-
In my code I have a class like this:
// created in main thread: class Model : public QObject { Q_OBJECT signals: void statusMessage(QString msg); public: void sendStatusMessage(QString msg) { // called from non main thread emit statusMessage(msg); } };This signal is connected to a slot that displays the message in the gui:
connect(model, // main thread &Model::statusMessage, this, // main thread &StatusText::addMessage);So I tell
connectthat the signal will be emitted from the main thread and still emits it from a non main thread.
Is this safe or undefined behaviour? It appears to be working.How can I achieve what I am trying to do in a safe manner?
Is it sufficient to add a
Qt::QueuedConnectionargument toconnect?If not I guess turning
sendMessage()into a slot and calling:QMetaObject::invokeMethod(m_model, "sendMessage", Qt::QueuedConnection, Q_ARG(QString, qmessage));from non main threads would work?
However I find that a not very elegant solution. -
@KroMignon: thank you for your help. From the documentation: "Qt::AutoConnection: If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used."
So there is an assymetry here. It checks in which thread the receiver lives:
receiver->thread().
But it do NOT check in which thread the sender lives (sender->thread()).
Instead it checks from which thread the signal is emitted:QThread::currentThread().So it seems that in which thread a
QObjectlives (affinity) and in which thread a method is running are two different things.
AQObjectlives in only one thread, which is the one it was created in (unless it has beenmoveToThread).
The methods of anQObjecton the other hand may be called from many different threads.Therefore the code is safe and the documentation explains why it is safe.
It is just that is a bit counterintuitive.@andyP said in Is it safe to emit a signal from another thread than the QObject?:
Therefore the code is safe and the documentation explains why it is safe.
It is just that is a bit counterintuitive.When multi-threading is used, there are many things which are not intuitive ;-)
Using signals/slots mechanism often simplifies data exchanges, but you have to be aware that, depending on parameter types, copies may be expensive! -
In my code I have a class like this:
// created in main thread: class Model : public QObject { Q_OBJECT signals: void statusMessage(QString msg); public: void sendStatusMessage(QString msg) { // called from non main thread emit statusMessage(msg); } };This signal is connected to a slot that displays the message in the gui:
connect(model, // main thread &Model::statusMessage, this, // main thread &StatusText::addMessage);So I tell
connectthat the signal will be emitted from the main thread and still emits it from a non main thread.
Is this safe or undefined behaviour? It appears to be working.How can I achieve what I am trying to do in a safe manner?
Is it sufficient to add a
Qt::QueuedConnectionargument toconnect?If not I guess turning
sendMessage()into a slot and calling:QMetaObject::invokeMethod(m_model, "sendMessage", Qt::QueuedConnection, Q_ARG(QString, qmessage));from non main threads would work?
However I find that a not very elegant solution.@andyP said in Is it safe to emit a signal from another thread than the QObject?:
So I tell connect that the signal will be emitted from the main thread and still emits it from a non main thread.
Not really, signal is from instance model to instance this, the used thread is not relevant for the connect statement. It will be checked when signal emitted.
As you should known, the instances may be moved to another thread at anytime, event after connect was done.How can I achieve what I am trying to do in a safe manner?
Why do you think this is not safe?
-
@andyP said in Is it safe to emit a signal from another thread than the QObject?:
So I tell connect that the signal will be emitted from the main thread and still emits it from a non main thread.
Not really, signal is from instance model to instance this, the used thread is not relevant for the connect statement. It will be checked when signal emitted.
As you should known, the instances may be moved to another thread at anytime, event after connect was done.How can I achieve what I am trying to do in a safe manner?
Why do you think this is not safe?
@KroMignon said in Is it safe to emit a signal from another thread than the QObject?:
Why do you think this is not safe?
Because sendStatusMessage(QString msg) is called from another thread (for whatever reason).
-
@KroMignon said in Is it safe to emit a signal from another thread than the QObject?:
Why do you think this is not safe?
Because sendStatusMessage(QString msg) is called from another thread (for whatever reason).
@jsulm said in Is it safe to emit a signal from another thread than the QObject?:
Because sendStatusMessage(QString msg) is called from another thread (for whatever reason).
Again, what is the problem?
msgis passed as copy, so I cannot see any problem. Maybe I am blind? -
@jsulm said in Is it safe to emit a signal from another thread than the QObject?:
Because sendStatusMessage(QString msg) is called from another thread (for whatever reason).
Again, what is the problem?
msgis passed as copy, so I cannot see any problem. Maybe I am blind?@KroMignon I also think there is no problem, just wanted to point out why OP is in doubt.
-
@andyP said in Is it safe to emit a signal from another thread than the QObject?:
So I tell connect that the signal will be emitted from the main thread and still emits it from a non main thread.
Not really, signal is from instance model to instance this, the used thread is not relevant for the connect statement. It will be checked when signal emitted.
As you should known, the instances may be moved to another thread at anytime, event after connect was done.How can I achieve what I am trying to do in a safe manner?
Why do you think this is not safe?
@KroMignon: thank you.
StatusText::addMessage()works on a QWidget so it should not be called from a non main thread?
Therefore a direct connection fromModel::statusMessage()toStatusText::addMessage()must not be made?
As you mention the thread of the sender and receiver is not checked when I callconnect()but rather when the signal is emitted.
But how is this done?
I do notmoveToThread()anywhere in my code.
I was assuming that theQObject::thread()would be called on both theModeland theStatusText.
I was also assuming that theQObject::thread()of aQObjectthat has not beenmoveToThread()would equal the thread in which theQObjectwas created.
The result of this test following my logic would be that both sender and receiver has affinity with the main thread and a direct call should therefore be made.
Which in turn should crash. But it does not. -
@KroMignon: thank you.
StatusText::addMessage()works on a QWidget so it should not be called from a non main thread?
Therefore a direct connection fromModel::statusMessage()toStatusText::addMessage()must not be made?
As you mention the thread of the sender and receiver is not checked when I callconnect()but rather when the signal is emitted.
But how is this done?
I do notmoveToThread()anywhere in my code.
I was assuming that theQObject::thread()would be called on both theModeland theStatusText.
I was also assuming that theQObject::thread()of aQObjectthat has not beenmoveToThread()would equal the thread in which theQObjectwas created.
The result of this test following my logic would be that both sender and receiver has affinity with the main thread and a direct call should therefore be made.
Which in turn should crash. But it does not.As you mention the thread of the sender and receiver is not checked when I call connect() but rather when the signal is emitted.
But how is this done?When you declare a
signal, the MOC (Meta Object Compiler) will create a function for you (cf. https://doc.qt.io/qt-5/why-moc.html).
"Emitting" as signal, is in fact calling this function. The Qtemitkeyword is in fact a#defineto nothing. It is a semantic suggar.I do not moveToThread() anywhere in my code.
I was assuming that the QObject::thread() would be called on both the Model and the StatusText.
I was also assuming that the QObject::thread() of a QObject that has not been moveToThread() would equal the thread in which the QObject was created.
The result of this test following my logic would be that both sender and receiver has affinity with the main thread and a direct call should therefore be made.
Which in turn should crash. But it does not.The code automatic generated by the moc will check in which thread the destination is running on signal call and will create copies of the parameters if required - depending of the connection type (direct, queued, auto) ==> cf https://doc.qt.io/qt-5/signalsandslots.html.
-
As you mention the thread of the sender and receiver is not checked when I call connect() but rather when the signal is emitted.
But how is this done?When you declare a
signal, the MOC (Meta Object Compiler) will create a function for you (cf. https://doc.qt.io/qt-5/why-moc.html).
"Emitting" as signal, is in fact calling this function. The Qtemitkeyword is in fact a#defineto nothing. It is a semantic suggar.I do not moveToThread() anywhere in my code.
I was assuming that the QObject::thread() would be called on both the Model and the StatusText.
I was also assuming that the QObject::thread() of a QObject that has not been moveToThread() would equal the thread in which the QObject was created.
The result of this test following my logic would be that both sender and receiver has affinity with the main thread and a direct call should therefore be made.
Which in turn should crash. But it does not.The code automatic generated by the moc will check in which thread the destination is running on signal call and will create copies of the parameters if required - depending of the connection type (direct, queued, auto) ==> cf https://doc.qt.io/qt-5/signalsandslots.html.
@KroMignon: thank you for your help. From the documentation: "Qt::AutoConnection: If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used."
So there is an assymetry here. It checks in which thread the receiver lives:
receiver->thread().
But it do NOT check in which thread the sender lives (sender->thread()).
Instead it checks from which thread the signal is emitted:QThread::currentThread().So it seems that in which thread a
QObjectlives (affinity) and in which thread a method is running are two different things.
AQObjectlives in only one thread, which is the one it was created in (unless it has beenmoveToThread).
The methods of anQObjecton the other hand may be called from many different threads.Therefore the code is safe and the documentation explains why it is safe.
It is just that is a bit counterintuitive. -
@KroMignon: thank you for your help. From the documentation: "Qt::AutoConnection: If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used."
So there is an assymetry here. It checks in which thread the receiver lives:
receiver->thread().
But it do NOT check in which thread the sender lives (sender->thread()).
Instead it checks from which thread the signal is emitted:QThread::currentThread().So it seems that in which thread a
QObjectlives (affinity) and in which thread a method is running are two different things.
AQObjectlives in only one thread, which is the one it was created in (unless it has beenmoveToThread).
The methods of anQObjecton the other hand may be called from many different threads.Therefore the code is safe and the documentation explains why it is safe.
It is just that is a bit counterintuitive.@andyP said in Is it safe to emit a signal from another thread than the QObject?:
Therefore the code is safe and the documentation explains why it is safe.
It is just that is a bit counterintuitive.When multi-threading is used, there are many things which are not intuitive ;-)
Using signals/slots mechanism often simplifies data exchanges, but you have to be aware that, depending on parameter types, copies may be expensive! -
@andyP said in Is it safe to emit a signal from another thread than the QObject?:
Therefore the code is safe and the documentation explains why it is safe.
It is just that is a bit counterintuitive.When multi-threading is used, there are many things which are not intuitive ;-)
Using signals/slots mechanism often simplifies data exchanges, but you have to be aware that, depending on parameter types, copies may be expensive!@KroMignon said in Is it safe to emit a signal from another thread than the QObject?:
Using signals/slots mechanism often simplifies data exchanges, but you have to be aware that, depending on parameter types, copies may be expensive!
@andyP and, if you pass custom classes/structs as parameters in the signals, you have to make sure they have a correct copy constructor/operator and are registers correctly with the metasytsem
-
@KroMignon said in Is it safe to emit a signal from another thread than the QObject?:
Using signals/slots mechanism often simplifies data exchanges, but you have to be aware that, depending on parameter types, copies may be expensive!
@andyP and, if you pass custom classes/structs as parameters in the signals, you have to make sure they have a correct copy constructor/operator and are registers correctly with the metasytsem
@J-Hilk: thank you. Regarding that I have several classes like:
struct PASHR { std::string utcTime; // hhmmss.ss double trueHeading = 999.0; int GNSSQuality = 0; enum class AlignmentStatus { GPSOnly = 0, CoarseLevelling = 1, DegradedSolution = 2, Aligned = 3, FullNavigationMode = 4 }; AlignmentStatus alignmentStatus = AlignmentStatus::GPSOnly; bool parseFromString(const std::string& line); }; Q_DECLARE_METATYPE(PASHR);Here is part of my
main():int main(int argc, char* argv[]) { QApplication a(argc, argv); qRegisterMetaType<std::string>(); qRegisterMetaType<PASHR>(); ... }I emit
PASHRby value across threads.
Is this safe?I am assuming that the default copy constructor and assignment operator are just fine?
Do I have to take some extra step regarding the
enum class? -
@J-Hilk: thank you. Regarding that I have several classes like:
struct PASHR { std::string utcTime; // hhmmss.ss double trueHeading = 999.0; int GNSSQuality = 0; enum class AlignmentStatus { GPSOnly = 0, CoarseLevelling = 1, DegradedSolution = 2, Aligned = 3, FullNavigationMode = 4 }; AlignmentStatus alignmentStatus = AlignmentStatus::GPSOnly; bool parseFromString(const std::string& line); }; Q_DECLARE_METATYPE(PASHR);Here is part of my
main():int main(int argc, char* argv[]) { QApplication a(argc, argv); qRegisterMetaType<std::string>(); qRegisterMetaType<PASHR>(); ... }I emit
PASHRby value across threads.
Is this safe?I am assuming that the default copy constructor and assignment operator are just fine?
Do I have to take some extra step regarding the
enum class?