Я не уверен, почему мой шаблон QThread блокируется

Я видел много сообщений и статей о QThread и перемещении QObject между QThreads, но, увы, это все еще вызывает у меня головную боль. Это шаблон, который я пытаюсь принять:

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

Теперь экземпляр этого создается в основном потоке графического интерфейса, но когда, наконец, вызывается loginProcess, он блокируется до завершения, что приводит к зависанию графического интерфейса моего приложения. Обратите внимание: никакой разницы не наблюдается, если я помещаю логический код непосредственно в функцию запуска и опускаю сигнал, как показано ниже:

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

Итак, я предположил, что мне нужно переместить «это» в поток с именем 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, и его функция запуска вызывается из другого потока. Давайте назовем этот другой поток GuiThread. Это означает, что GuiThread имеет контроль, поэтому должен иметь возможность передать право собственности на экземпляр ConnectionThread рабочему потоку.

И последняя возможность, которую я еще не исследовал полностью, — это возможность перемещения m_connectionPtr в рабочий поток.

Любые мысли о приведенном выше шаблоне, как я могу его улучшить и вообще, как я могу предотвратить его блокировку?


person Ben J    schedule 18.04.2013    source источник
comment
Является ли нить соединения производной от чего-либо? Является ли ConnectionPtr классом, выполняющим работу, которую вы хотите выполнить в другом потоке? Опять же, является ли conncetionPtr производным от чего-либо. Из образца, который вы опубликовали, не совсем ясно...   -  person Ilya Kobelevskiy    schedule 18.04.2013
comment
Я думаю, что решающим является то, что на самом деле объект ConnectionThread не должен быть перемещен в отдельный поток. Я не вижу, какие действия вы хотели бы делегировать работнику. Теперь я не вижу ничего, что происходит в worker.   -  person Adrian    schedule 19.04.2013
comment
ConnectionThread является производным от QObject. И ConnectionPtr, который также является производным от QObject, выполняет ту работу, которую я хочу выполнить в другом потоке.   -  person Ben J    schedule 19.04.2013


Ответы (1)


РЕДАКТИРОВАТЬ 1: Следующее было предложенным мной решением, но на самом деле оно не работает должным образом, потому что сигнал Finish() никогда не испускается рабочим процессом

РЕДАКТИРОВАТЬ 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», который сам является производным от QObject, но не имеет родителя; аналогично ConnectionThread является производным от QObject, но опять же не имеет родителя).

Поскольку m_connectionPtr также будет использоваться другими потоками в будущем, мне также пришлось снова переместить его обратно в основной поток, как показано в слоте moveConnectionPtrBack.

Кажется, помогает, но в целом не совсем без ошибок.

person Ben J    schedule 19.04.2013