Hi, I'm using Qt to power an application server with multithreaded TCP listener and maintenence tasks running in separate threads. The TCP listener runs in main thread and spawns a separate thread to handle socket operation. The socket is being created when the thread is running using a socket descriptor passed from the TCP listener. The maintenance threads hold a timer only and run on timer shot to query DB and do stuff (doesn't really matter).
The thing is, I have always used it like this: // [L1] Listing 1 // this simplified sample code neglects cleanup and possible memory leaks class MaintenanceWorker : public QObject { Q_OBJECT public: MaintenanceWorker() : QObject(NULL), mTimer(NULL) { // executes in main thread moveToThread(&mThread); connect(&mThread, SIGNAL(started()), this, SLOT(onThreadStarted())); } private slots: void onThreadStarted() { // executes in spawned thread mTimer = new QTimer(); connect(mTimer, SIGNAL(timeout()), this, SLOT(onTimerTimeout())); mTimer->start(60000); } void onTimerTimeout() { // executes in spawned thread } private: QThread mThread; QTimer* mTimer; // to be created in thread }; After porting the project from Qt 4 to Qt 5 (using Qt 5.3 now) I've decided to use high-level QtConcurrent facilities and namely QThreadPool for its ability to reuse threads, control pool size and so on. So the sample code above was transformed into this: // [L2] Listing 2 // this simplified sample code neglects cleanup and possible memory leaks class MaintenanceWorker : public QObject, public QRunnable { Q_OBJECT public: MaintenanceWorker() : QObject(NULL), mTimer(NULL) { // executes in main thread } void run() { // executes in spawned thread mTimer = new QTimer(); connect(mTimer, SIGNAL(timeout()), this, SLOT(onTimerTimeout())); mTimer->start(60000); QEventLoop loop; loop.exec(); } void onTimerTimeout() { // executes in main thread (!!!) } private: QTimer* mTimer; // to be created in thread }; // ... QThreadPool threadPool; threadPool.start(new MaintenanceWorker()); So the problem is that onTimerTimeout() slot runs in main thread (checked with QThread::currentThread()). After reading the docs carefully -- which had to be the first thing to do, my bad -- I've found that [cite1]: - *The child of a QObject <http://doc.qt.io/qt-5/qobject.html> must always be created in the thread where the parent was created.* This implies, among other things, that you should never pass the QThread <http://doc.qt.io/qt-5/qthread.html> object (this) as the parent of an object created in the thread (since the QThread <http://doc.qt.io/qt-5/qthread.html> object itself was created in another thread). - *Event driven objects may only be used in a single thread.* Specifically, this applies to the timer mechanism <http://doc.qt.io/qt-5/timers.html> and the network module <http://doc.qt.io/qt-5/qtnetwork-module.html>. For example, you cannot start a timer or connect a socket in a thread that is not the object's thread <http://doc.qt.io/qt-5/qobject.html#thread>. It turns out that in case of [L2] MaintenanceWorker's thread is main thread but in case of [L1] MaintenanceWorker's thread is the created QThread thanks to moveToThread() call. Could anyone please tell if my conclusions are true? If so, it's perfectly fine with me since I've found the explanation in the docs but is there any feasible way of using timers inside QRunnables started with QThreadPool? I've taken a quick look at QThreadPool sources and didn't find anything moving the runnable to thread. This can be worked around if connection type is explicitly set to DirectConnection when connecting timer slot but is it a good practice? Another important thing for me is network. The docs also specify that the network module should also follow single thread policy. Does that mean that an instance of QTcpSocket can't be used in QRunnable started by QThreadPool? Thanks Cheers Dmitriy [cite1] http://doc.qt.io/qt-5/threads-qobject.html#signals-and-slots-across-threads
_______________________________________________ Interest mailing list Interest@qt-project.org http://lists.qt-project.org/mailman/listinfo/interest