0

我看过很多关于 QThread 和在 QThreads 之间移动 QObjects 的帖子和文章,但唉,它仍然让我头疼。这是我试图采用的模式:

#include "connectionthread.h"
#include <cassert>

ConnectionThread::ConnectionThread(ConnectionPtr const &connectionPtr) :
                               worker(NULL),
                               m_connectionPtr(connectionPtr)
{
    connect(this, SIGNAL(executeSignal()), this, SLOT(loginProcess()));
}

void
ConnectionThread::start()
{
    if(worker) {
        if(worker->isRunning()) {
            worker->quit();
        }
        delete worker;
    }
    worker = new QThread;
    connect(worker, SIGNAL(started()), this, SLOT(run()));
    worker->start();
}

void
ConnectionThread::run()
{
    emit executeSignal();
}

void
ConnectionThread::loginProcess()
{
    m_connectionPtr->Connect();
}

现在在主 GUI 线程中创建了一个实例,但是当最终调用 loginProcess 时,它会阻塞直到完成,这会导致我的应用程序的 GUI 挂起。请注意,如果我将逻辑代码直接放入 run 函数并省略如下信号,则不会观察到差异:-

void
ConnectionThread::run() 
{
    m_connectionPtr->Connect();
}

所以我假设我需要将“this”移动到名为 worker 的线程中,例如:

void
ConnectionThread::start()
{
    if(worker) {
        if(worker->isRunning()) {
            worker->quit();
        }
        delete worker;
    }
    worker = new QThread;
    this->moveToThread(worker);
    connect(worker, SIGNAL(started()), this, SLOT(run()));
    worker->start();
}

但这给了我

QObject: Cannot create children for a parent that is in a different thread.

我不确定为什么会这样,但是因为创建了 ConnectionThread 的一个实例并且它的 start 函数是从另一个线程调用的。让我们称这个其他线程为 GuiThread。这意味着 GuiThread 具有控制权,因此应该能够将 ConnectionThread 实例的所有权转移给工作线程。

我尚未完全探索的最后一种可能性是将 m_connectionPtr 移动到工作线程的可能性。

关于上述模式的任何想法,我如何改进它,以及一般我如何防止它阻塞?

4

1 回答 1

0

编辑 1:以下是我提出的解决方案,但它实际上并没有按预期工作,因为 worker 永远不会发出 finished() 信号

编辑 2:修复了触发的完成信号,但我仍然无法将 m_connectionPtr 移回 moveConnectionPtrBack 内的主线程。给出错误“QObject::moveToThread: 当前线程 (0x102900380) 不是对象的线程 (0x10493b740)。无法移动到目标线程 (0x102900380)”

所以,我想我已经知道该怎么做:解决方案似乎是将 ConnectionPtr 的线程所有权转移到工作线程:

#include "connectionthread.h"

ConnectionThread::ConnectionThread(ConnectionPtr const &connectionPtr) :
                               worker(NULL),
                               m_connectionPtr(connectionPtr)
{
    // EDIT 2 added bit -- m_connectionPtr sends signal when work finished
    connect(m_connectionPtr.data(), 
            SIGNAL(connectFinishedSignal()), this, SLOT(quitThread()));
}

void
ConnectionThread::start()
{
    if(worker) {
        if(worker->isRunning()) {
            worker->quit();
        }
        delete worker;
    }
    worker = new QThread;
    m_connectionPtr->moveToThread(worker);
    connect(worker, SIGNAL(started()), m_connectionPtr.data(), SLOT(Connect()));
    connect(worker, SIGNAL(finished()), this, SLOT(moveConnectionPtrBack()));
    worker->start();
}

void
ConnectionThread::moveConnectionPtrBack()
{
    // this call failing still
    m_connectionPtr->moveToThread(QApplication::instance()->thread());
}

// EDIT 2 added bit; quitting causes worker to send finished signal() which causes
// correct triggering of moveConnectionPtrBack() function
void
ConnectionThread::quitThread()
{
    worker->quit();
}

(注意 m_connectionPtr 是“Connection”的共享 ptr,它本身是从 QObject 派生的,但没有父级;同样,ConnectionThread 是从 QObject 派生的,但同样没有父级)。

由于 m_connectionPtr 将来也被其他线程使用,我还必须将它再次移回主线程,如 moveConnectionPtrBack 插槽所示。

似乎可以解决问题,但总体而言并非完全没有错误。

于 2013-04-19T09:33:28.760 回答