【问题标题】:QObject: Cannot create children for a parent that is in a different threadQObject:无法为位于不同线程中的父级创建子级
【发布时间】:2011-08-01 15:49:45
【问题描述】:

编辑:

我尝试按照你们在 cmets 中告诉我的操作...:

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_buildrepos\qt-desktop\src\corelib\thread\qthread_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();

为什么会这样?如何解决?

【问题讨论】:

  • 这个主题有一篇很好的文章Threads, Events and QObjects。如果你的 Citizen 类在线程中工作,你不应该从 QThread 继承它,因为从 QThread 继承的目的不是在线程中做一些工作,而是管理线程。

标签: c++ multithreading qt asynchronous qnetworkaccessmanager


【解决方案1】:
QObject: Cannot create children for a parent that is in a different thread.

您得到这个是因为您在 Citizen 的构造函数中创建 QNetworkAccessmanager,该构造函数在“原始”线程中被调用。当 Citizen 对象被移动到新线程时,QNetworkAccessmanager 仍然将其线程关联设置为原始线程,但在运行调用中,它将尝试在新线程中创建 QNetworkReply 对象(可能还有其他对象)。这会产生上述警告。

如果您在运行槽中(或在 Citizen 对象移动到新线程后的任何时间)创建管理器,则不会发生这种情况。

但是您仍然有一些问题。例如,Citizen 类实际上不需要是 QThread。它不必要地使它复杂化。将 QObject 子类化就足以满足您的目的(afaict)。只需制作一个普通插槽并将其连接到 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");

【讨论】:

    【解决方案2】:

    我将尝试回答您为什么会看到 QThread: Destroyed while thread is still running 错误。

    如果你这样做

    void mtMethod () {
    
     Citizen * c = new Citizen(this);
     QThread thread;
     c->moveToThread(&thread);
    
     connect(&thread, SIGNAL(started()), c, SLOT(ProcessActions()));
     thread.start();
    }
    

    退出函数时线程对象将被销毁,但已启动的线程仍在运行!。 Qt 警告您应该停止线程或在更大范围内创建线程对象。 (即使其成为您班级的成员函数)。像这样:

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

    【讨论】:

    • 将父对象分配给然后移动到另一个线程的对象是不行的。这个答案是不正确的。 @Kim Bowles Sørhus 的答案应该是这样标记的。
    【解决方案3】:

    在调用 run 之前,我不相信新线程存在。所以构造函数是一个不同于run()的线程。如果将管理器对象的创建从构造函数移到 run() 会发生什么?我想这将解决第一个错误,如果不是计时器错误也是如此。

    另外,我认为很多人仍在按照您的方式构建线程,但您可能想查看this

    【讨论】:

    • 不起作用,我还查看了该博客文章。检查我的问题。
    • 哪个没用?将管理器的创建移出构造函数,或者以链接讨论的方式使用 moveToThread?我不认为你的编辑是正确的。如果您使用 Citizen 作为移动到 QThread 的对象,则 Citizen 不应派生自 QThread。
    • 首先,您的 QThread,在堆栈上创建,在您退出子程序后销毁,启动线程。你不想要这个,我想。你应该让 QThread 成为一个类的成员。然后,您无需显式调用quit 方法。当exec 返回时,您的线程将停止。
    • @Brett Stottlemyer 将管理器的创建移出构造函数不起作用。 moveToThread 也没有,但正如您指出的那样,我可能使用不正确。我会按照你的方式尝试。
    • @Brett 我将 Citizen 类设为 QObject,但它仍然无法正常工作。
    【解决方案4】:

    你需要考虑thread affinity。该错误消息不是说谎或疯狂,它准确地告诉您出了什么问题。

    【讨论】:

    • 你的链接失效了。
    【解决方案5】:

    您的问题主要是由于尝试继承 QThread。即使文档推荐它,它也不是使用 QThread 的最佳方式。请参阅this question and answer 了解更多信息和链接。

    【讨论】:

      【解决方案6】:

      我还没有弄清楚 startTimers 错误,尽管它可能与第一个错误有关。无论如何,您应该能够修复第一个错误。我在 Qt 中遇到过几次这个问题,我发现这是解决它的“最佳”方法是创建一个初始化函数和一个清理函数。类的所有成员都是在调用 run 之前初始化为 NULL 的指针。请注意,“最好”用引号引起来,因为肯定会有不同的意见,但它适用于我的大多数情况。

      标题

      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*)));
      }
      

      【讨论】:

        猜你喜欢
        • 2012-06-09
        • 2011-03-17
        • 2016-07-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多