views:

1607

answers:

4

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's 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?

A: 

It looks like your worker thread completes before you can even invoke any function or send a signal to it.

steve
the worker thread is in it's event loop because of the call to exec()
Idan K
+1  A: 

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.

Idan K
+1  A: 

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

TimW
A: 

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();
    }
};
Ropez