全部博文(584)
分类:
2011-01-19 22:42:07
We use IRC extensively to communicate amongst ourselves as well as with the community. I hang out on the #qt channel on the Freenode network and help people with questions when I can. A common question that I see (and that makes me cringe at the same time) has to do with understanding threading with Qt and how to make some code they’ve written work. People show their code, or examples based on their code, and I often end up thinking:
You’re doing it wrong
I know this is a bit of a bold thing to say, perhaps a bit provocative, but at the same time, I can’t help but think that the (hypothetical) class below is an incorrect application of object-oriented principles as well as incorrect usage of Qt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class MyThread : public QThread { public : MyThread() { moveToThread( this ); } void run(); signals: void progress( int ); void dataReady( QByteArray ); public slots: void doWork(); void timeoutHandler(); }; |
My #1 biggest gripe with this code is moveToThread(this);
I see so many people using this without understanding what it does. What does it do, you ask? The moveToThread()
function tells Qt to ensure that event handlers, and by extension
signals and slots, are called from the specified thread context. QThread
is the thread interface, so we’re telling the thread to run code “in itself”. We’re also doing this before
the thread is running as well. Even though this seems to work, it’s
confusing, and not how QThread was designed to be used (all of the
functions in QThread were written and intended to be called from the
creating thread, not the thread that QThread starts).
My impression is that moveToThread(this);
creeps into
people’s code because they saw some blog somewhere that used it. A quick
web search turns up several of these blogs, all of which follow the
pattern in the class above:
moveToThread(this);
and comments that “it seems to work when I add this”In my opinion, the problems started at step 1. QThread was designed and is intended to be used as an interface or a control point to an operating system thread, not as a place to put code that you want to run in a thread. We object-oriented programmers subclass because we want to extend or specialize the base class functionality. The only valid reasons I can think of for subclassing QThread is to add functionality that QThread doesn’t have, e.g. perhaps providing a pointer to memory to use as the thread’s stack, or possibly adding real-time interfaces/support. Code to download a file, or to query a database, or to do any other kind of processing should not be added to a subclass of QThread; it should be encapsulated in an object of it’s own.
Usually, this means simply changing your class to inherit from
QObject instead of QThread and, possibly, changing the class name.
QThread has a started()
signal that you can connect to when
you need to perform some initialization. To actually have your code run
in the new thread context, you need to instantiate a QThread and assign
your object to that thread using the moveToThread()
function. Even though you are still using moveToThread()
to tell Qt to run your code in a specific thread context, we are
keeping the thread interface separate. If necessary, it is now possible
to have multiple instances of your class assigned to a single thread, or
multiple instances of many different classes assigned to a single
thread. In other words, it’s unnecessary to tie a single instance of a
class to a single thread.
–
I take much of the blame for the confusion that comes with writing threaded Qt code. The original QThread class was abstract, so subclassing was necessary. It wasn’t until Qt 4.4 that QThread::run() gained a default implementation. Previously, the only way to use QThread was to subclass. With the addition of thread affinity and support for signal and slot connections between objects of different affinity, suddenly we have a convenient way of working with threads. We like convenience, we want to use it. Unfortunately, I realized to late that forcing people to subclass QThread actually made it harder than it needed to be.
I also take the blame for not getting up-to-date examples and
documentation made to show people how to get the convenience with the
minimum amount of headaches. For now, the best resource I can point at
is
a blog I wrote several years ago.
–
Disclaimer: everything you see above is of course opinion. I’ve worked a lot on these classes, and have a fairly clear idea of how to use them and how not to use them.