Reputation: 81
I have 2 Threads in a Qt5Application:
Thread A: contains a bunch of QObject derived class objects
Thread B: worker in this Thread has all the pointers to the objects in A
Thread A might be very busy at times and Thread B is only there to delegate Signals and manage some other stuff. It never writes to any of these objects, but I need to check some getter functions which return booleans from the objects in A.
in ThreadB:
if (objInThrA->isFinished()) { ... }
The isFinished() returns a boolean.
If Thread A is really busy in a function and I call these isFinished functions in Thread B, will my Thread B get stalled until Thread A is finished with its work, or will this work?
Upvotes: 8
Views: 4748
Reputation: 773
The short answer is no.
When you run as described, you're directly calling methods from an object which is not in your thread (the object is in Thread A but you're calling one of its methods from Thread B). Directly calling a method is not something Qt has modified from standard C++, so it doesn't operate through signals, slots, or the event loop and is ignorant of your threading model. That means that if you call the method in Thread B, it runs in Thread B.
Calling methods on an object from another thread can be dangerous if you aren't careful about it because it introduces problems with concurrency. If Thread A is in the middle of updating the _mFinished data member when Thread B calls getFinished() on it, it may get a partially written value. In your specific example it happens to be that there is no "partially written" state for the boolean and you'll probably be fine.
Resolving concurrency problems is done through atomic operations on the elements which will be shared between multiple threads. Either you can use variables that are guaranteed to be atomic read and write operations, like booleans usually are, or you can use locking mechanisms to protect those variables. Mutexes and Semaphores are the most common locking mechanism and allow locking from multiple threads to limit access to the variables while they're being read and written. This is done in such a way as to avoid them being partially written with a new value when a read of the variable occurs.
I'd recommend reading up on mutexes and semaphores (generic multithreaded data structures) if you're doing work that involves more than one thread since they're critical to understanding threading in general. Additionally Qt has nice Qt wrapped versions of them that keep them simple to use and avoid some easy pitfalls in maintaining their usage (e.g. QMutexLocker).
Upvotes: 0
Reputation: 15196
Qt signals and slots can be used between different threads. But there are two rules:
Last argument of connect()
should be either Qt::QueuedConection
or Qt::BlockingQueuedConnection
(or defaults to Qt::AutoConnection
, which is the same as Qt::QueuedConnection
if objects belong to different threads). QueuedConnection
means that emitter does not wait signal processing to be completed, BlockingQueuedConnection
means it does.
QObject-derived classes are not suitable for passing between threads. They should be safely copied before that (see QMetaType docs).
Upvotes: 4
Reputation: 32685
You should never access members or directly call functions of an object which in another thread. The only safe way is to access them through signal/slot mechanism. You can have a signal in ThreadB
and connect it to a slot in ThreadA
which returns the value:
connect(objInThrB, SIGNAL(getFinished()), objInThrA, SLOT(isFinished()), Qt::BlockingQueuedConnection);
This way when you emit the signal in thread B like:
bool ret = getFinished();
the slot in thread A would be called when control returns to the event loop of thread A and the value would be returned. Thread B waits for the slot to be called, that's because the connection type is BlockingQueuedConnection
. So be aware not to block the application main thread using this kind of blocking connection.
Upvotes: 1
Reputation: 81
Ok, so I tested it myself.
In Thread B:
connect(this,SIGNAL(runWork()),objInThrA,SLOT(doWork()));
emit runWork();
QThread::sleep(2);
qDebug() << objInThrA->isFinished();
in Thread A:
qDebug() << "start A sleep";
QThread::sleep(10);
qDebug() << "end A sleep";
OUTPUT:
start A sleep
false
end A sleep
It works, however Im still unsure if I use it this way its correctly done and defined behavior.
Upvotes: 0