Вызов методов в контексте QThread - PullRequest
5 голосов
/ 01 августа 2009

В моем приложении есть основной поток и рабочий поток (QThread).
Из основного потока я бы хотел вызвать метод моего рабочего потока и запустить его в контексте потока.

Я пытался использовать QMetaObject::invokeMethod и дать ему опцию QueuedConnection, но она не работает.
Я также попытался передать сигналы из основного потока (который подключен к слоту рабочего потока), но это также не удалось.

Вот пример примерно того, что я пробовал:

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

Используя способ QMetaObject:

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

Использование сигнального пути:

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

В обоих случаях идентификатор основного потока будет напечатан в QThread doWork.

Кроме того, я подумал о реализации простого производителя-потребителя, но если это работает, есть ли какая-то причина, почему бы не сделать это таким образом?

Ответы [ 5 ]

3 голосов
/ 01 августа 2009

Проблема заключалась в том, что получатель (QThread) «живет» в основном потоке, и, следовательно, цикл событий основного потока является тем, который выполняет слот.

из документации Qt:

При подключениях в очереди слот вызывается, когда управление возвращается в цикл событий потока, которому принадлежит объект. Слот выполняется в потоке, в котором находится объект-получатель.

Таким образом, решение, которое я нашел до сих пор, состояло в том, чтобы создать объект внутри run () потока и использовать вместо него его слоты. Таким образом, владельцем получателя является поток, а затем слот вызывается в контексте потоков.

2 голосов
/ 11 августа 2009

В этом примере показано, как можно разделить класс Worker, чтобы он работал так, как вы хотите. Вам также необходимо предоставить ссылку или указатель на экземпляр Worker, чтобы иметь возможность подключиться к слоту.

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();
    }
};
2 голосов
/ 02 августа 2009

Для простого примера производителя-потребителя, посмотрите на запись в блоге от Брэдли Т. Хьюза Поступайте без головной боли .

1 голос
/ 20 июня 2012

Рабочий создается в главном потоке, и поэтому его события обрабатываются в основном потоке. Вы должны переместить Worker в его собственный поток:

Worker worker;
worker.moveToThread(&worker);
worker.start();

Теперь Qt знает, что worker живет в новом потоке, и будет помещать в очередь события в этом цикле событий.

0 голосов
/ 01 августа 2009

Похоже, ваш рабочий поток завершает работу еще до того, как вы сможете вызвать какую-либо функцию или отправить ей сигнал.

...