Search code examples
c++multithreadingqtqthread

Invoking methods in QThread's context


In my application there's the main thread and a worker thread (QThread).
From the main thread I'd like to invoke a method of my worker thread and have it run in the thread's context.

I've tried using QMetaObject::invokeMethod and give it the QueuedConnection option but it's not working.
I've also tried emitting signals from the main thread (which is connected to the worker thread's slot) but that also failed.

Here's a snippet of roughly what I tried:

class Worker : public QThread
{
    Q_OBJECT

public:
    Worker() { }

    void run() 
    { 
        qDebug() << "new thread id " << QThread::currentThreadId(); 
        exec(); 
    }

public slots:
    void doWork()
    {
        qDebug() << "executing thread id - " << QThread::currentThreadId();
    }
};

Using the QMetaObject way:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main thread id - " << QThread::currentThreadId();

    Worker worker;
    worker.start();

    QMetaObject::invokeMethod(&worker, "doWork", Qt::QueuedConnection);

    return a.exec();
}

Using the signal way:

class Dummy : public QObject
{
    Q_OBJECT

public:
    Dummy() { }

public slots:
    void askWork() { emit work(); }

signals:
    void work();
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main thread id - " << QThread::currentThreadId();

    Worker worker;
    worker.start();

    Dummy dummy;
    QObject::connect(&dummy, SIGNAL(work()), &worker, SLOT(doWork()), Qt::QueuedConnection);

    QTimer::singleShot(1000, &dummy, SLOT(askWork()));

    return a.exec();
}

Both ways result in the main thread id being printed in the QThread doWork.

Also, I thought of implementing a simple producer-consumer but if this works, is there any reason why not to do it this way?


Solution

  • The problem was that the receiver (the QThread) 'lives' in the main thread and thus the main thread's event loop is the one that executes the slot.

    from Qt's docs:

    With queued connections, the slot is invoked when control returns to the event loop of the thread to which the object belongs. The slot is executed in the thread where the receiver object lives.

    So the solution I found so far was to create an object inside the thread's run() and use its slots instead. That way the receiver's owner is the thread and then the slot is called in the threads context.