Discussion about "Threads, Events and QObjects" article
-
That is wrong, it depends, what you are doing...
Me, for example,, I often have worker threads, that do not use signal / slot, but that are waiting on wait conditions and have order queues which are filled by the client via special methods. These threads overwritre run (as the yhave special handling there) and implement synchronisation on their own.
Aslo if you have pre/post conditions in your threads event loop (like COM initialization on Windows), you have to overwrite run. So there are many scenarios where it makes sense...
-
Great article peppe. One area you didn't get into was implicitly shared objects. This is a tricky subject and one that I wrote about before:
http://www.folding-hyperspace.com/program_tip_15.htm
While I don't know if my analysis is absolutely correct, I think this is another area where the official Qt documentation needs improvement. They totally missed that QDateTime is implicitly shared and when I tried using it in threads I found the problem noted above. The same thing applies to list objects like QList. Passing these between threads in signals and slots might be a problem, but I haven't explored this yet. I simply protect all such objects with QMutex and haven't seen any problems.
Also, here is a more complete article I wrote on getting good timing in threads:
http://www.folding-hyperspace.com/program_tip_14.htm
The Qt_RealtimeIO_App example download shows several ways of doing timing and how accurate or inaccurate it can be.
A more complete list of soft realtime related articles is at:
http://www.folding-hyperspace.com/program_tips.htm
I will read your article more carefully in time and see if I can suggest any other improvements.
And I might say it is really fun to program up a big numerical program using QConcurrent and run it on an Intel i7 with 8 cores and watch it speed up x8 times. Qt rocks!
-
Dear James,
your analysis is incorrect. QString, QDateTime and many others (possibly all?) implicitly shared classes clearly state in the documentation that they're not thread safe. Or, better, they say they're only reentrant, therefore you can't assume they're thread safe. You are describing a race condition between two threads accessing the same object at the same time, which causes a major problem.
That is, suppose you have something like:
@
SharedObj *obj = new SharedObj;
@Then thread 1 is executing
@
delete obj; // runs the dtor
@And at the same time thread 2 is executing
@
SharedObj obj2(*obj); // runs the copy ctor
@"obj" is now being accessed from two threads at the same time: from the obj2 copy ctor and from its dtor. This violates the contract: SharedObj is not thread-safe; up to one thread can access a certain instance at any time.
What instead implicitly sharing lets you to do is, for instance:
@
SharedObj obj1(...);
SharedObj obj2 = obj1;
/* now thread1 accesses obj1 while thread2 accesses obj2 */
@The two objects will safely share the internal data (a memory optimization), but as soon as one thread tries a non-const access, the object detaches its internal data performing a deep copy. This is guaranteed to work (and indeed does).
-
[quote author="peppe" date="1299361797"]
That is, suppose you have something like:
@
SharedObj *obj = new SharedObj;
@Then thread 1 is executing
@
delete obj; // runs the dtor
@And at the same time thread 2 is executing
@
SharedObj obj2(*obj); // runs the copy ctor
@[/quote]Indeed so. Note that this is an approach that in almost all cases shouldn't be used for implicitly shared classes. They do the memory tricks so you don't have to. -
[quote author="Volker" date="1298497988"]The two articles are identical. My "version" (that without the underscores) is only a link to the actual article. The reason is, that the old version of the link is referred in some other articles and in a blog entry.
Unfortunately it does not redirect to the actual article but pulls in its content and it does not leave a message of doing so.[/quote]
I totally missed what happened :) Thank you for setting everything up properly!
-
Sorry for taking a year to get back Peppe, but thanks for your reply at http://qt-project.org/forums/viewreply/25457/. I tend to get busy with big projects and don't worry about past problems I've already fixed.
It took me a few minutes to get my head around what you were saying but after carefully re-reading the Qt documentation it finally hit me. It does make sense that if the objects are copied like you show in the first thread (or using a mutex if from another thread) then things are safe. This also says that passing a static copy of an object to another thread using a signal is safe because the copy operation happens on the original thread. Signaling with a pointer to the original object is another matter, however, and should not be done.
I guess my real gripe was the fact that QDateTime was not listed as implicitly shared when it really was. You can copy some static objects like QDate or QTime in the way I was doing without crashing because they only contain static members and are not implicitly shared. One might not get the right result due to synchronization problems with the underlying data, but it won't cause a crash.
I will update my article soon with a link back to here.
-
I think now I start understaing in the relevant "wiki page":http://qt-project.org/wiki/Threads_Events_QObjects . Thanks great articles!
After reading it second time while I'm translating it into Korean, I found following statement is quite ambiguous.
bq.
A thread event loop delivers events for all QObjects that are living in that thread; this includes, by default, all objects that are created into that thread, or that were moved to that thread (more info about this later). We also say that the thread affinity of a QObject is a certain thread, meaning that the object is living in that thread. This applies to objects which are built in the constructor of a QThread object:Because, any object that are built in the constructor of QThread couldn't live the thread represented by that QThread object, I feel like that the last statement sounds misleading doesn't it?
-
[quote author="Andre" date="1331800273"]Not really, as those are not created in that thread. Note that thread != QThread. The constructor of QThread runs in the same thread as the code that created the QThread object itself, not in the thread that QThread manages. [/quote]
Yeah, so the last statement might be "This doesn't apply to objects which are built in the constructorof a QThread object:" I thought.
-
No, I think you are misunderstanding this piece of text and the accociated piece of code. The fact that objects live in the thread that created them also applies to those objects created in the constructor of a subclassed QThread, where the thread is still the thread creating the object. If you read on, you will see an example that explains this further. The text below explains exactly this issue. So really, the text is correct.
-
So an object created in the constructor of a QThread (or subclass of QThread) is in the context of the thread that created the QThread object. Objects which are created in the run(..) method of the QThread object or which are moved to the context of the thread, which is managed by the QThread object, are in the context of that ("new") thread.
Correct ?
-
Thx 4 this great article!
There are still two things, which are not clear to me.
- How do I stop the thread's event loop, if it's executing an inifitely task (like reading data from serial port)? (It is not busy all the time, but it needs to run the event loop to be notified, when new data arrives).
Before reading this article I always did thread->moveToThread(thread); with the thread and then later called QMetaObject::invokeMethod(thread, "quit");
without moveToThread(thread) this won't work.
Maybe your wondering why I read the serial port from a different thread. The reason is, that when I'm moving the application's window with the mouse, the serial port won't receive any data until I release the window. It's probably some kind of bug in QextSerialPort, but I was able to fix it by using a thread for reading the serial port (but still event based).
- I'm not sure if I got it write, how it is determined, if Queued or DirectConnection is used.
My current understanding is that when using AutoConnection on every emit it is checked, if the thread which executes the emit statement is the same as the thread in which the objects whichs slot is going to be invoked lives in. If the threads are the same, then the slot is called directly, otherwise the invocation is queued.
Before reading this article I always thought, that at the moment at which QObject::connect with AutoConnection is called, it is determined, if a Direct or Queuedconnection is used and then this connection type will be used for all further emits.
Is the threadaffinity checked on every emit, or just when doing connect?
- How do I stop the thread's event loop, if it's executing an inifitely task (like reading data from serial port)? (It is not busy all the time, but it needs to run the event loop to be notified, when new data arrives).
-
in this section
[quote]
A much better and simpler way of achieving the same result is simply using timers, i.e. a QTimer [doc.qt.nokia.com] object with a 1s timeout, and make the doWork() method a slot:class Worker : public QObject
{
Q_OBJECTpublic:
Worker() {
connect(&timer, SIGNAL(timeout()), this, SLOT(doWork()));
timer.start(1000);
}private slots:
void doWork() {
/* ... */
}private:
QTimer timer;
};All we need is a running event loop, then the doWork() method will be invoked each second.
[/quote]
the timer needs to have its parent set to Worker instance otherwise it wont change thread when the owning class instance does. -
Hello Peppe, I've started revamping the official overview documentation for thread usage in Qt, and I'd like to incorporate some of the information from your article. Is that ok?
-
Is it OK to spawn new thread inside a readyRead() handler and then access and write to tcpsockets ( from the parent thread ) from that thread ? It looks to me that this is not good because sometimes clients are not receiving data . What is the proper way to do this ?
-
[quote author="great88" date="1376933337"]Is it OK to spawn new thread inside a readyRead() handler and then access and write to tcpsockets ( from the parent thread ) from that thread ? It looks to me that this is not good because sometimes clients are not receiving data . What is the proper way to do this ?[/quote]If your TCP socket is in the parent thread, you cannot write to it from a new thread.
There have been many posts about combining QTcpSocket and QThread recently. Search the forum, and you should find plenty of info.