2011-08-01 37 views
7

編輯:的QObject:不能爲父母是在不同的線程創建兒童

我試着做什麼你們告訴我在評論...:

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

當管理器 - > 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()獲取e xecuted:

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

我開始這個線程以下列方式:

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

爲什麼會出現這種情況?如何解決這個問題?

+0

有這個主題[線程,事件和的QObject]良好的製品(http://qt-project.org/wiki/ Threads_Events_QObjects#913fb94dd61f1a62fc809f8d842c3afa)。如果你的二等公民工作在一個線程,你不應該的QThread繼承,因爲繼承從QThread的目的不是做在線程一些工作,但管理線程。 –

回答

3

我只是嘗試回答爲什麼你看到的QThread:被毀壞,而線程仍在運行錯誤。

如果你這樣做,當你退出的功能,但已啓動仍然運行的線程這

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

將一個父對象分配給一個被移動到另一個線程的對象是不行的。這個答案是不正確的。 @Kim BowlesSørhus的答案是應該被標記的那個。 – rbaleksandar

1

我不相信新的線程存在,直到運行被調用。所以構造函數與run()不同。如果將創建的管理器對象從構造函數移動到run(),會發生什麼?我想這將解決第一個錯誤,如果不是計時器錯誤。

此外,我認爲很多人仍然按照您的方式構建線程,但您可能需要檢查this

+0

不起作用,我也查出了那篇博文。檢查我的問題。 – Ahmed

+0

哪個沒用?將管理器的創建從構造函數中移出,或者按照鏈接討論的方式使用moveToThread?我不認爲你的編輯是正確的。如果您使用Citizen作爲移動到QThread的對象,Citizen不應該從QThread派生。 –

+0

第一,你的QThread,棧上創建的,你退出子程序,啓動線程後銷燬。我想,你不需要這個。你應該讓QThread成爲一個班級的成員。 然後,你需要顯式調用'quit'方法。 'exec'返回時,你的線程將停止。 – Lol4t0

1

您需要考慮thread affinity。那個錯誤信息不是說謊或者瘋狂的,它告訴你究竟是有什麼問題。

+0

請再次檢查我的問題,我編輯它... – Ahmed

+0

你的鏈接已經死了。 – Akiva

1

你的問題主要是由於嘗試子類化QThread。儘管文檔推薦它,但它不是使用QThread的最佳方式。請參閱this question and answer瞭解更多信息和鏈接。

1

我還沒有想出startTimers錯誤,雖然它可能與第一個有關。無論如何,你應該能夠修復第一個錯誤。我已經在Qt中遇到了這個問題幾次,我發現這是解決它的最好方法是創建一個初始化函數和一個cleanUp函數。該類的所有成員都是指向被初始化爲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*))); 
} 
10
QObject: Cannot create children for a parent that is in a different thread. 

你得到這一點,因爲你是在公民的構造,這是被稱爲在「原始」的線程創建QNetworkAccessmanager。當公民對象移動到新線程的QNetworkAccessmanager仍然有它的線程關聯設置爲原始線程,但在運行調用它會嘗試創建新線程的QNetworkReply對象(以及可能藏漢其他對象)。其中產生上面的警告。

如果您創建在運行插槽經理(或公民對象之後的任何點移動到新的線程)不會發生的。

但是你仍然有一些問題。例如,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"); 
相關問題