QObject: не удается создать дочерние элементы для родителя, который находится в другом потоке

EDIT:

Я пытался делать то, что вы, ребята, сказали мне в комментариях ... :

Citizen * c = new Citizen(this);

QThread thread;
c->moveToThread(&thread);

connect(&thread, SIGNAL(started()), c, SLOT(ProcessActions()));
thread.start();

это приводит к еще большим ошибкам:

QThread: Destroyed while thread is still running
ASSERT failure in QThread::setTerminationEnabled(): "Current thread was not started with QThread.", file c:ndk_buildreposqt-desktopsrccorelibthreadqthread_win.cpp, line 542
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
QObject::killTimers: timers cannot be stopped from another thread

у меня проблемы с этой ошибкой ... Я застрял на этом уже 2 дня и не могу получить решение.

заголовок:

class Citizen : public QThread
{
Q_OBJECT    
    QNetworkAccessManager * manager;

private slots:
    void onReplyFinished(QNetworkReply* net_reply);

public:
    Citizen(QObject * parent);

    void run();
};

реализация:

Citizen::Citizen(QObject * parent)
{
    manager = new QNetworkAccessManager;
    connect(_net_acc_mgr, SIGNAL(finished(QNetworkReply*)),
            this, SLOT(onReplyFinished(QNetworkReply*)));
}

void Citizen::onReplyFinished(QNetworkReply* net_reply)
{
    emit onFinished(net_reply);
}

void Citizen::run()
{
    manager->get(QNetworkRequest(QUrl("http://google.com"));

    QEventLoop eLoop;
    connect(manager, SIGNAL( finished( QNetworkReply * ) ), &eLoop, SLOT(quit()));
    eLoop.exec(QEventLoop::ExcludeUserInputEvents);

    qDebug() << "loaded google!";

    exec();
}

когда manager - >get () выполняется, я получаю следующее ошибка:

QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0xc996cf8), parent's thread is QThread(0xaba48d8), current thread is Citizen(0xca7ae08)

когда eLoop.exec () выполняется:

QObject::startTimer: timers cannot be started from another thread

Я начинаю этот поток следующим образом:

Citizen * c = new Citizen(this);
c->start();

почему это происходит? Как это решить?

6 ответов


Я просто попытаюсь ответить, почему вы видите QThread: уничтожен, пока поток все еще работает ошибка.

Если вы делаете это

void mtMethod () {

 Citizen * c = new Citizen(this);
 QThread thread;
 c->moveToThread(&thread);

 connect(&thread, SIGNAL(started()), c, SLOT(ProcessActions()));
 thread.start();
}

объект thread будет уничтожен при выходе из функции, но поток, который был запущен, все еще работает !. Qt предупреждает Вас, что вы должны либо остановить поток, либо создать объект потока в большей области. (Я. e сделайте его функцией-членом вашего класса). Что-то вроде этого :

class myClass
{
virtual ~myClass ();
 QThread mythread;
};

myClass::~myClass
{
  mythread.stop ();
}

void myClass::mtMethod () {

     Citizen * c = new Citizen(this);
     c->moveToThread(&mythread);

     connect(&mythread, SIGNAL(started()), c, SLOT(ProcessActions()));
     mythread.start();
}

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

вы получаете это, потому что вы создаете QNetworkAccessmanager в конструкторе Citizen, который вызывается в "исходном" потоке. Когда объект Citizen перемещается в новый поток, QNetworkAccessManager по-прежнему имеет сродство потока к исходному потоку, но в вызове run он попытается создать объект QNetworkReply ( и, возможно, другие объекты также ) в новом потоке. Что дает предупреждение выше.

Если вы создаете менеджер в слоте run (или в любой момент после перемещения объекта Citizen в новый поток) этого не произойдет.

однако у вас все еще есть некоторые проблемы. Например, класс Citizen действительно не должен быть QThread. Это напрасно все усложняет. Для вашей цели(afaict) достаточно подкласса QObject. Просто сделайте обычный слот и подключите его к сигналу QThread::started (). И, как отметил OrcunC, вам нужно убедиться, что экземпляр QThread правильно прицел.

для больше на продевать нитку:http://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/

пример:

QThread *thread = new QThread;
thread->start();
Citizen *worker = new Citizen;
worker->moveToThread(thread);

//startWorking can be equivalent of the run function
//in your current implementation and this is where you should
//create the QNetworkAccessManager
QMetaObject::invokeMethod(worker,"startWorking");

Я не верю, что новый поток существует, пока не будет вызван run. Таким образом, конструктор является другим потоком, чем run(). Что произойдет, если переместить создание объекта manager из конструктора в run()? Я предполагаю, что это исправит первую ошибку, если не ошибку таймера.

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


вам нужно рассмотреть потоку. Это сообщение об ошибке не является ложью или безумием, оно говорит вам ровно то, что случилось.


ваши проблемы в основном связаны с попыткой подкласса QThread. Несмотря на то, что документация рекомендует это, это не лучший способ использовать QThread. Пожалуйста, смотрите этот вопрос и ответ для получения дополнительной информации и ссылок.


Я не понял ошибку startTimers, хотя она может быть связана с первой. В любом случае, вы сможете исправить первую ошибку. Я столкнулись с этой проблемой в Qt несколько раз, и я считаю, что это лучший способ обойти это, чтобы создать функцию инициализации и функцию очистки. Все члены класса являются указателями, которые инициализируются до NULL, пока не будет вызван run. Обратите внимание, что "лучший" находится в кавычках, потому что наверняка будут разные мнения, но это подходит для большинства ситуаций.

заголовок

class Citizen : public QThread {
   Q_OBJECT

   QNetworkAccessManager * manager;

   private slots:
      void onReplyFinished(QNetworkReply* net_reply);
   public:
      Citizen(QObject * parent);
      void run();

   private:
      void initialize();
      void cleanUp();
 };

реализация

Citizen::Citizen(QObject * parent) :
   manager(NULL) {
}

void Citizen::onReplyFinished(QNetworkReply* net_reply) {
   emit onFinished(net_reply);
}

void Citizen::run() {
   initialize();
   manager->get(QNetworkRequest(QUrl("http://google.com"));

   QEventLoop eLoop;
   connect(manager, SIGNAL( finished( QNetworkReply * ) ),
           &eLoop, SLOT(quit()));
   eLoop.exec(QEventLoop::ExcludeUserInputEvents);

   qDebug() << "loaded google!";
   exec();

   cleanUp();
}

void Citizen::initialize() {
   manager = new QNetworkAccessManager;
   connect(_net_acc_mgr, SIGNAL(finished(QNetworkReply*)),
           this, SLOT(onReplyFinished(QNetworkReply*)));
}

void Citizen::cleanUp() {
   delete manager;
   disconnect(_net_acc_mgr, SIGNAL(finished(QNetworkReply*)),
              this, SLOT(onReplyFinished(QNetworkReply*)));
}