在QThreadPool中执行插槽

时间:2013-07-26 07:33:20

标签: c++ multithreading qt threadpool qeventloop

我有一个应该在一个线程中运行的类,并且需要一个插槽的事件循环,目前我使用moveToThread()很好地运行它,但是我想使用QThreadPool我遇到过一个问题。

当使用QThreadPool运行时,我的runnable的run()方法是从一个池化的线程调用的(我用QThread::currentThread()检查),但我的插槽没有在池线程中运行,所以我认为该对象不会移动到池中的线程。

我认为这是因为我知道插槽是在接收者的线程中运行的,这正是我在使用moveToThread()方法和QThread时得到的(正确)行为。

如何让我的QRunnable(下例中的Foo)完全在池线程中运行? 或者是我做错了什么或理解错了?

以下POC演示了这个问题:

foo.h中

#ifndef FOO_H
#define FOO_H

#include <QObject>
#include <QRunnable>
#include <QEventLoop>

class Foo : public QObject, public QRunnable
{
    Q_OBJECT
public:
    explicit Foo(int data, QObject *parent = 0);
    void run();
signals:
    void startWorking();
public slots:
    void doWork();

private:
    QEventLoop eventLoop;
    int data;
};

#endif // FOO_H

Foo.cpp中

#include "foo.h"

#include <QThread>
#include <QDebug>

Foo::Foo(int d, QObject *parent) :
    QObject(parent), eventLoop(this), data(d)
{
}

void Foo::run()
{
    qDebug() << "run() in: " << QThread::currentThread();
    connect(this, SIGNAL(startWorking()), this, SLOT(doWork()));
    emit startWorking();
    eventLoop.exec();
}

void Foo::doWork()
{
    qDebug() << "doWork() in: " << QThread::currentThread();
}

的main.cpp

#include <QCoreApplication>
#include <QThreadPool>

#include "foo.h"

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

    Foo *foo = new Foo(42);

    QThreadPool::globalInstance()->start(foo);

    return a.exec();
}

但请注意,在我的实际代码中,信号不会立即发出,因为它将在我在网络上收到一些数据之后。

PS:也可以找到POC here

3 个答案:

答案 0 :(得分:2)

也许你可以将class Foo中的逻辑分成两部分:
在调用QRunnable方法之前,您在QEventLoop的工作线程上创建的QObject run()QEventLoop::exec工作人员QThreadPool。然后将所有信号转发给工作对象。 所以现在你的插槽将在汇集的线程上被调用 但是,QThread用于执行大量短任务而不会创建太多同时线程。有些任务排队等待其他任务完成。如果这不是您的意图,您可能需要回到旧的{{1}}并使用它。

答案 1 :(得分:0)

您可以同时支持两种模式,但需要外部的一些协调。我的策略是从Date.toLocaleString内部发出通过当前线程的信号。当您打算在线程池中使用它时,请在此信号上使用QRunnable::run并在其中执行Qt::BlockingQueuedConnection。否则,将其移至moveToThread并发出信号以开始照常工作。

TaskRunner.h

QThread

TaskRunner.cpp

#pragma once

#include <QObject>
#include <QRunnable>
#include <QThread>

class TaskRunner : public QObject, public QRunnable
{
  Q_OBJECT
public:
  TaskRunner(int data, QObject* parent = nullptr);

  void run() override;

Q_SIGNALS:

  void start();
  void starting(QThread*);
  void stop();

private:
  int data;
};

main.cpp

#include "TaskRunner.h"
#include <QEventLoop>
#include <stdexcept>


TaskRunner::TaskRunner(int data, QObject* parent)
: QObject(parent), data(data)
{
    // start should call run in the associated thread
    QObject::connect(this, &TaskRunner::start, this, &TaskRunner::run);
}

void TaskRunner::run()
{
    // in a thread pool, give a chance to move us to the current thread
    Q_EMIT starting(QThread::currentThread());

    if (thread() != QThread::currentThread())
      throw std::logic_error("Not associated with proper thread.");

    QEventLoop loop;
    QObject::connect(this, &TaskRunner::stop, &loop, &QEventLoop::quit);
    // other logic here perhaps
    loop.exec();
}

答案 2 :(得分:-1)

自连接电话

connect(this, SIGNAL(startWorking()), this, SLOT(doWork()));

使用连接类型的默认参数,它将是Qt :: Autoconnection。 信号从池化线程发出,并且槽仍然属于foo,foo与主线程具有线程亲和性。 autoconnection将决定将插槽放在主线程的事件队列中。

有两种方法可以解决这个问题:

1

connect(this, SIGNAL(startWorking()), this, SLOT(doWork()), Qt::DirectConnection);

并删除eventloop.exec();

2

在run方法中,在连接信号和插槽之前将foo对象移动到当前线程。