QThread moveToThread 不起作用

Posted

技术标签:

【中文标题】QThread moveToThread 不起作用【英文标题】:QThread moveToThread doesn't work 【发布时间】:2017-08-10 07:02:55 【问题描述】:

我有一个用于 GUI 的主线程,在其中运行 MainWindow 对象, 在其构造函数中,我创建了一个新的 worker 对象和一个 QThread 对象,然后我将 worker 移动到线程,问题是在打印它们的 id 时它们是相同的:

MainWindow.cpp

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)

    std::cout<<"MAIN_ID "<< QThread::currentThreadId()<<std::endl;
    QThread *t_pc = new QThread;
    worker *pc_w;
    pc_w = new pc_worker();
    pc_w->moveToThread(t_pc);
    t_pc->start();
    pc_w->initialize();
    // ...

worker.cpp

worker::worker(QObject *parent) : QObject(parent) 



void worker::initialize() 
    std::cout << "INITIALIZE " << QThread::currentThreadId() << std::endl;

我明白了:

MAIN_ID 0x7f4009ccb780
INITIALIZE 0x7f4009ccb780

怎么了?

【问题讨论】:

但是,当通过connect SLOT/SIGNAL调用worker类时,打印的ID是不同的 当你直接调用一个函数时,它会在你调用它的线程中运行。 将 initialize() 定义为一个槽,并从 MainWindow 发出一个信号。像这样连接它们:connect(this, SIGNAL(signal_initialize()), pc_w, SLOT(initialize()), Qt::QueuedConnection); 【参考方案1】:

答案:moveToThread确实工作,只是不像你预期的那样。

看起来在调用 pc_w-&gt;moveToThread(t_pc) 之后,您希望现在在 t_pc 中调用 pc_w 的所有成员函数。但是,moveToThread() 不是这样做的。

moveToThread() 的目的是更改QObject“线程亲和性”,换句话说就是对象所在的线程。 但是在基本层面上,它为您提供的一切只是保证通过Qt::QueuedConnection 连接到任何信号的所有对象插槽将在其中调用(运行)特定线程。

成员函数仍然在您调用它们的线程中运行。在您的情况下,您从 GUI 线程调用 initialize(),因此 QThread::currentThreadId() 为您提供该线程的 id

我真的建议阅读有关线程事件循环的thread affinity 和this article 的官方文档。

QThread* thread = new QThread;
Worker* worker = new Worker;

// Uses Qt::AutoConnection (default)
// which will be transalted into Qt::QueuedConnection
QObject::connect(thread, &QThread::started, worker, &Worker::initialize);

std::cout<<"MAIN_ID "<< QThread::currentThreadId()<<std::endl;

worker->moveToThread(thread);
thread->start();

输出:

MAIN_ID 0000000000003E5C
INITIALIZE 0000000000003DAC

trivelt提出的解决方案人为地将“call initialize()”事件放入线程事件循环中,达到同样的效果。但是,它不执行任何编译时检查(“initialize”被指定为字符串)。

【讨论】:

【参考方案2】:

尝试使用invokeMethod,而不是直接调用pc_w-&gt;initialize(),例如:

QMetaObject::invokeMethod(pc_w, "initialize", Qt::QueuedConnection);

在这种情况下,您的 initialize() 方法应该是 SLOT 或 SIGNAL。

【讨论】:

以上是关于QThread moveToThread 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 moveToThread() 在 pyqt 中正确使用 QThread?

QThread - 使用 moveToThread 将类成员移动到线程

重点:怎样正确的使用QThread类(注:包括推荐使用QThread线程的新方法QObject::moveToThread)

Qt线程—QThread的使用--run和movetoThread的用法

[QThread]moveToThread()方法创建子线程只能用signal-slot来调用函数

QThread多线程编程经典案例分析(三种方法,解释了为什么使用moveToThread的根本原因,即为了避免调用QThread::exec() )