6

I'm trying to use threads in Qt to delegate some work to a thread, but I can't get it to work. I have a class inheriting QMainWindow that have a member object that launch threads to do work. This object has the QMainwindow as parent. It contains and initialize notably another QObject, the m_poller, which I want to move to the thread I create :

m_pollThread = new QThread;
m_poller->moveToThread(m_pollThread);
//Bunch of connection
m_pollThread->start();

I followed the guidelines about how to manage thread in Qt without subclassing it (aka not doing it wrong), but I still get the following message in VS :

QObject::moveToThread: Current thread (0x2dfa40) is not the object's thread (0x120cf5c0). Cannot move to target thread (0x1209b520)

I found the following post that seems to deal with the same issue, but couldn't fix my code with the answer. I feel like I'm actually calling moveToThread correctly (as in I don't call it from within another thread to "pull" an object to it), but apparently I'm still missing something there: as the message hints, it seems there's already multiple thread and my call to moveToThread() seems to end up in the wrong one (though I admit I'm completely new to this and could figure this out completely wrong...)

So what could still be possibly wrong with the way I use Qt threads ?

Thanks !

Community
  • 1
  • 1
JBL
  • 12,588
  • 4
  • 53
  • 84

4 Answers4

9

You can only use moveToThread in case when

  • Your object has no parent (because otherwise the parent will have different thread affinity)
  • You are on the object's owner thread so you actually 'push' the object from current thread to another

So your error message says you're violating the second case. You should call moveToThread from the thread that created the object.
And according to you

This object has the QMainwindow as parent.

So moveToThread will not work, again. You should remove the parent from m_poller object

spiritwolfform
  • 2,263
  • 15
  • 16
  • I've read the Qt doc, and the `m_poller` object has actually no parent. The object that holds the m_poller has one on the contrary (but my post is quite ambiguous...) – JBL Jun 06 '13 at 11:08
  • in this case everything seems right with your code, you can also check if QThread::currentThread() == m_poller::thread() to make sure you're using the moveToThread in a right way. What happens if you create m_poller object right before you use moveToThread? – spiritwolfform Jun 06 '13 at 11:12
2

You can also move it to your thread by doing it from the object's owner thread.

#include <thread>
#include <memory>
#include <condition_variable>
#include <QTimer>
#include <QThread>
#include <QApplication>


template <typename Func>
inline void runOnThread(QThread *qThread, Func &&func)
{
    QTimer *t = new QTimer();
    t->moveToThread(qThread);
    t->setSingleShot(true);
    QObject::connect(t, &QTimer::timeout, [=]()
    {
        func();
        t->deleteLater();
    });
    QMetaObject::invokeMethod(t, "start", Qt::QueuedConnection, Q_ARG(int, 0));
}



void moveToThread(QObject *ptr, QThread *targetThrd=QThread::currentThread())
{
    std::mutex mt;
    std::condition_variable_any cv;
    runOnThread(ptr->thread(),[&]
    {
        ptr->setParent(NULL);
        ptr->moveToThread(targetThrd);
        cv.notify_one();
    });
    cv.wait(mt);
}

You just need to call

moveToThread( m_poller, m_pollThread);
pPanda_beta
  • 618
  • 7
  • 10
  • 1
    Very clever, and works! Just one thing (at least for C++17), before the `cv.wait()` there needs to be a `std::lock_guard lck(mt);` then `cv.wait(lck);` instead of waiting on the mutex directly (mutex needs to be locked, w/out that it throws an exception). One can also use `QWaitCondition` with a `QMutex` and `QMutexLocker` instead, with pretty much identical syntax. – Maxim Paperno Dec 06 '22 at 22:06
1

I think the problem is with the initialization of m_poller, which according to the error message seems to be assigned to a different (third) thread from the one that is executing your code snippet.

Also, if this code is executed multiple times, it may work the first time but then fail on subsequent times as m_poller no longer belongs to the executing thread, but rather m_pollThread.

hmn
  • 716
  • 7
  • 18
  • `m_poller` is a custom QObject which is initialized in the same object that then create the thread and call `moveToThread()` (I'll edit for clarity). – JBL Jun 06 '13 at 11:00
  • I have edited my answer to reflect the fact that this may still happen even if m_poller is orginially created in the "main" thread. – hmn Jun 06 '13 at 11:01
  • How can the initialization "assign" `m_poller` to a different thread that the one it is initialized in ? (Note taken about the multiple executions of this code, very good point indeed, though this happens the first time the code is executed). – JBL Jun 06 '13 at 11:03
  • Hard to say without knowing the code... I think you will have to carefully track the execution of your code to see when the thread affinity of m_poller changes. – hmn Jun 06 '13 at 11:23
0

If you are moving your object through signals and slots (you have created your m_poller in one thread and called a signal and passed it to a slot of another object which is not in caller thread) make sure to use Qt::DirectConnection type for your connect. In this way your slot will execute in the caller thread and calling moveToThread is in the caller thread.

Mohammad Rahimi
  • 965
  • 5
  • 15