Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 235
Idan K Avatar asked Aug 01 '09 13:08

Idan K


3 Answers

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.

like image 75
Idan K Avatar answered Sep 19 '22 06:09

Idan K


This example shows how you can split up the Worker class, to make it work as you want. You also need to make available a reference or pointer to the Worker instance to be able to connect to the slot.

class Worker : public QObject
{
    Q_OBJECT

public:
    Worker() { }

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

class WorkerThread : public QThread
{
    Q_OBJECT

public:
    void run()
    {
        qDebug() << "new thread id " << QThread::currentThreadId(); 
        Worker worker;
        exec();
    }
};
like image 30
Ropez Avatar answered Sep 19 '22 06:09

Ropez


For the simple producer-consumer example, have a look at the blog entry from Bradley T. Hughes Treading without the headache.

like image 41
TimW Avatar answered Sep 20 '22 06:09

TimW