Reputation: 577
I am new to QThread and multithreading, so I am not sure if I am doing it correctly. The program has not crashed so far, but I want to check if I am doing it correctly. I have some code as follows (MyThreadClass is inherited from QThread):
std::vector<MyThreadClass* > workThreads;
for(int i=0;i<Solutions.size();i++)
{
workThreads.push_back(new MyThreadClass(Solutions[i]));
}
for(int i=0;i<workThreads.size();i++)
{
connect(workThreads[i], SIGNAL(finished()), this, SLOT(onFinished()));
workThreads[i]->start();
}
bool finished = false;
while(!finished)
{
if(m_finishedThread==workThreads.size())
finished=true;
this->msleep(10);
}
The onFinished function is given as follows:
void MyClass::onFinished()
{
++m_finishedThread;
}
So you can see that the while loop is waiting for all the threads to finish and updated the m_finishedThread variables. Is this a safe way of doing it? If all threads finish their job and trying to "connect" to onFinished() function, will it cause a problem?
Upvotes: 1
Views: 3987
Reputation: 1824
First of all, you should not subclass QThread
directly. See:
https://www.qt.io/blog/2010/06/17/youre-doing-it-wrong
Create a normal class instead (a subclass of QObject
or similar) and implement your code there. Then use moveToThread()
to move the subclass to that thread.
It is safe to connect signals to slots in your object using a Qt::QueuedConnection
, as long as the passed parameters are not objects and you are not calling methods on those objects without any synchronization mechanism.
Anyway, what you want to do is probably easier to implement using QtConcurrent
.
Upvotes: 1
Reputation: 4029
I probably would not run the while(!finished) loop, since this hangs your mainthread (or executing thread of the while loop). Instead check in onFinished() for running threads and emit a signal when everything is finished.
If you need to wait for all threads to be finished there is still wait()-Condition.
For modifying a variable you can use a Qt:QueuedConnection with a signal to modify a variable if it is not directly accesible.
Be careful with accessing variables from multiple threads directly. It is very often a good idea to use a QMutex when accessing the same variable from different threads.
Upvotes: 1
Reputation: 22724
That's safe. The trick is that the connection you will establish between your QThread subclasses and this
will be a queued connection. That's because the thread that emits the signal finished
(*) is different from the thread this
(the receiver) lives in.
A queued connection is implemented via event posting. A special event is posted to the event queue of the thread this
lives in; the handling of that event is calling your slot. So, your slot will only be accessed
this
lives inand that's obviously thread safe.
By the way, is that your real code? You could do exactly the same with
for (int i = 0; i < numThreads; ++i)
threads[i]->wait();
(*) I'm talking about the thread which emits the signal, not the affinity of the object that emits the signal. In particular, chances are that your QThread subclass objects are living in the very same thread than this
!
Where's the catch? The thread which emits finished
is not the one those objects (and this
) are living in -- is the thread managed by those objects.
Addendum (2): this is the code that implements signal emission. As you can see, the check is against the thread currently running vs. the thread the receiver lives in. The thread the sender lives in is not taken into account, so you don't need to do stuff like thread->moveToThread(thread)
to make a queued connection.
How's it possible that the thread currently running isn't the same thread the sender lives in? Because that's exactly what QThread does: the QThread object lives in one thread, the thread it manages (and which emits finished()
!) is another thread:
// runs in the MANAGED thread; lives in any thread
void QThread::run_in_another_thread() {
run(); // user-supplied implementation
emit finished();
}
Addendun to the addendum: but then, are you relying on the undocumented knowledge that finished
gets emitted from another thread? Isn't that relying on the implementation?
The answer is no. There's only one other choice: finished()
gets emitted as part of a reaping/cleanup handling, coming from the event loop the QThread object is leaving. That is, the same thread this
is living in.
It can't come from anywhere else -- if we're running user code, we aren't running anything else (remember that one thread can't be running two different things).
That implies:
this
lives in. You need this in any case.this
lives in. So, we're just calling a function within the same thread; by definition, that's thread safe. So, this changes absolutely nothing about the way we handle this. It requires an active event loop in this
, and that's it.
Addendum: I should've read the code better. This doesn't change what I said above, but:
bool finished = false;
while(!finished)
{
if(m_finishedThread==workThreads.size())
finished=true;
this->msleep(10);
}
This loop here never returns to the event loop. This means your slot will never be invoked because the metacall events will never be processed. Please use a QTimer
or sneak in some calls to QCoreApplication::processEvents
instead of this kind of loops!
Upvotes: 4
Reputation: 12907
At first sight I'd say no. Indeed, you've connected your threads finished()
signal without specifying the last parameter of connect (which is the ConnectionType )
As you can see from the documentation, if you don't specify this parameter, Qt::AutoConnection
is the default one, and specify that the program will behave differently if the signal comes from an object in the same thread, or in a different thread.
In this case, Qt::QueuedConnection
will be used for these calls, which means that when the signal will trigger a call to the slot, this call be queued in the event loop of the receiving thread. If multiple thread finish at the same time, calls to onFinished()
will be queued, and invoked one by one.
EDIT: In addition, I suggest you consider a different approach for your design based on thread, and read this which says you should rather create a worker object and then move it to the thread.
Upvotes: 0
Reputation: 1021
nop, seems ok, Qt's signal/slots system is thread safe, actually, when a signal is emitted, it's put at the end of a queue, and is processed by the main event loop in that order.
Upvotes: -2