0

I have an Qt5 c++ app with 2 threads, thread A is started when the main program starts up. The start method of thread A runs successfully.

So far so good. Next, in the main program I send a signal to Thread A to start a QTimer, which it does - but that timer never expires!

Thread B handles tcp connections. When I initiate a telnet connection to my app, thread B fires up and suddenly I see my Qtimer from thread A expiring at normal intervals.

Why is the QTimer from thread A not expiring until thread B starts?

I suspect my threads are getting messed up. note the last section of code below products this:

thread of this:  QThread(0x200fe00)  
thread of timer:  QThread(0x1fff470)

Which suggest my worker object (this), is in a different thread from my timer object. This timer thread address is actually the MAIN thread. Why? I'm confused.

Suggestions?


In my main app I create and start my thread like this:

QThread * MyControllerThread = new QThread(this);

if (MyControllerThread) {

    TheController *worker = new TheController(MyControllerThread);

    if (worker) {
        connect(MyControllerThread, SIGNAL(started()), worker, SLOT(start()));
        connect(MyControllerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
        connect(MyControllerThread, SIGNAL(finished()), MyControllerThread, SLOT(deleteLater()));
        worker->moveToThread(MyControllerThread);  
        MyControllerThread->start();  
    } 

and in my main app I emit a signal to the new thread:

    emit sig_startlocalpeer(Types::EActionLocalServiceStart);  // Move the local peer to standby mode to start remote tests

which runs a slot in my thread (TheController object):

connect(&m_remotetestintervaltimer,SIGNAL(timeout()),this,SLOT(expiredRemoteTestIntervalTimer()));
m_remotetestintervaltimer.setTimerType(Qt::VeryCoarseTimer);
m_remotetestintervaltimer.start(REMOTETEST_TIMER_INTERVAL);  // Wait between ticks
qDebug() << "thread of this: " << this->thread();
qDebug() << "thread of timer: " << m_remotetestintervaltimer.thread();
TSG
  • 4,242
  • 9
  • 61
  • 121
  • I already call the start() method of the thread. Where do I call run() from? Do I need to call both? – TSG Aug 10 '14 at 00:57
  • Could you please post a small code sample to illustrate you problem? – Tanuki Aug 10 '14 at 01:06
  • Because I create 2 threads, a main app, and the timer is started in a slot, there's a lot of code to post. I'll post if no easy A-HA answers are posted...but hoping to avoid a really big post – TSG Aug 10 '14 at 01:11
  • Sorry, start() is the correct method to call. – Chris Aug 10 '14 at 01:24
  • 1
    You haven't posted enough of code to tell what's wrong - as you've made clear in your comments to one of the answers. Such incomplete questions are not useful to anyone else, and are off-topic here. I really stress the **importance of posting an [SSCCE](http://sscce.org)** in a question. Copy, paste, (compile,) see is the aim. Otherwise we waste time on asking you questions that wouldn't be needed otherwise, and it takes much longer for *you* to get an answer. Thus you waste our time and your own time. This is clearly counterproductive. – Kuba hasn't forgotten Monica Aug 10 '14 at 17:30
  • See my answer here: https://stackoverflow.com/a/46778252/7076615 – MathCrackExchange Oct 16 '17 at 20:15

2 Answers2

2

Well, it's not a Qt5 bug, it's more an inaccurate understanding of Qt's thread spirit.

In Qt, you have two ways to implement a thread which are using or not an even loop. Here is just a small visual example.

No event loop

myMethodCalledInANewThread
{
    do{ ... }while(...);
}

With an event loop

myMethodCalledInANewThread
{
    [...]
    exec();
}

(Of course you can mix a do/while with an even loop but stay simple).

In QTimer's doc, you can read:

In multithreaded applications, you can use QTimer in any thread that has an event loop. [...] Qt uses the timer's thread affinity to determine which thread will emit the timeout() signal. Because of this, you must start and stop the timer in its thread; it is not possible to start a timer from another thread.

So I'm pretty sure you don't have a second event loop in your second thread and that's why you have the behaviour you described.

To give you some tips to be totally clear with thread using Qt, I suggest you to read:

and a very good article about how QThread implementation is misunderstood by a lot of users:

I hope it will help ;)

ololuki
  • 377
  • 1
  • 7
  • 14
Martin
  • 877
  • 8
  • 20
  • I read both docs, and I still looks like I was doing everything right. The timer was started/stopped in the new thread A. However, the Qtimer was instantiated (not started) while part of the main thread and moved to thread A (and then started). – TSG Aug 10 '14 at 13:17
  • 1
    As of Qt 4.8 it looks like you no longer have to call exec in the thread either. Start slot will start an event loop for the thread automatically. – TSG Aug 10 '14 at 15:19
  • Huum okay, it's hard to help you without seeing enough code (as kuba said above !). Can you upload a simple project to reproduce your problem please ? Thank you :) – Martin Aug 10 '14 at 21:07
-1

The best answer seems to be a combination of RobbieE and Kuba:

You have to explicitly set the parent of the member variable in constructor. The parent-child feature is a Qt thing that exists among classes derived from QObject, it is not a feature of C++.

I never knew this - I assumed that when an object was created, its members variables automatically had their parent set to the object. Good to know!!

TSG
  • 4,242
  • 9
  • 61
  • 121
  • 1
    Is your QTimer object parentless or is the worker class its parent? When you move a Qbject to another thread, only itself and child objects are moved to the new thread. All other member objects created in the main thread before the move remain aligned to the main thread. – RobbieE Aug 10 '14 at 08:17
  • Interesting idea...the QTimer was a member variable in my worker class. So its parent SHOULD have been the worker object. When the worker object was moved QTimer should have moved with it. Sounds right? – TSG Aug 10 '14 at 13:18
  • "So its parent SHOULD have been the worker object." No. To understand this, please explain how can an object instance know where is it instantiated (local variable, instance member, class member, heap). Of course *it can't*, there's no provision for that in C++. In addition to having the timer be a member of the worker object, you also must pass it (`this`) as a parent in the worker's constructor: `Worker::Worker(QObject*parent) : m_timer(this) { ... }` – Kuba hasn't forgotten Monica Aug 10 '14 at 17:29
  • When I instantiate the class, I get an object with member variables (including the QTimer). If I move the object to a new thread, shouldn't the member variables move with it? – TSG Aug 11 '14 at 03:16
  • @GenerationDSystems No. Only objects that have the object you're moving to a new thread set as their parent, i.e. its children. – thuga Aug 11 '14 at 08:59
  • Does that mean member variables defined in the class , do not have the object automatically set as their parent when the class is instantiated? So, one would have to change the parent of member variables after the class is intantiated? – TSG Aug 11 '14 at 14:15
  • They do not. You have to explicitly set the parent in the member initialisation list of the constructor (as Kuba Ober illustrated), if the member is a static member, or at the time of creation, if you create it on the heap. The parent-child feature is a Qt thing that exists among classes derived from QObject, it is not a feature of C++. – RobbieE Aug 11 '14 at 18:11