2011-08-01 32 views
7

EDIT:QObject: Không thể tạo trẻ em cho cha mẹ đó là trong một thread khác

tôi đã cố gắng làm những gì cậu nói với tôi trong ý kiến ​​...:

Citizen * c = new Citizen(this); 

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

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

Điều này tạo ra nhiều hơn lỗi:

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 

Tôi đang gặp sự cố với lỗi này ... Tôi bị kẹt trong 2 ngày này và không thể có giải pháp.

Tiêu đề:

class Citizen : public QThread 
{ 
Q_OBJECT  
    QNetworkAccessManager * manager; 

private slots: 
    void onReplyFinished(QNetworkReply* net_reply); 

public: 
    Citizen(QObject * parent); 

    void run(); 
}; 

Thực hiện:

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

Khi Manager-> get() được thực thi, tôi nhận được lỗi sau:

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) 

Khi eLoop.exec() được e xecuted:

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

tôi bắt đầu chủ đề này theo cách sau đây:

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

Tại sao điều này xảy ra? Làm thế nào để giải quyết điều này?

+0

Có một bài viết tốt cho chủ đề này [Chủ đề, sự kiện và QObjects] (http://qt-project.org/wiki/ Threads_Events_QObjects # 913fb94dd61f1a62fc809f8d842c3afa). Nếu lớp của bạn Citizen làm việc trong một chủ đề bạn không nên kế thừa nó từ QThread, bởi vì mục đích thừa kế từ QThread không phải là để làm một số công việc trong thread, nhưng quản lý các chủ đề. –

Trả lời

3

tôi sẽ chỉ cố gắng trả lời lý do tại sao bạn đang nhìn thấy QThread: Destroyed trong khi chủ đề vẫn chạy lỗi.

Nếu bạn làm điều này

void mtMethod() { 

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

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

Đối tượng chủ đề sẽ bị phá hủy khi bạn thoát khỏi chức năng nhưng các chủ đề đã được bắt đầu vẫn chạy!. Qt cảnh báo bạn rằng bạn nên dừng thread hoặc tạo đối tượng thread trong phạm vi lớn hơn. (nghĩa là biến nó thành một hàm thành viên trong lớp của bạn). Một cái gì đó như thế này:

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

Gán một phụ huynh cho một đối tượng mà sau đó được chuyển sang một chủ đề khác là không có. Câu trả lời này không đúng. Câu trả lời của @Kim Bowles Sørhus là câu trả lời nên được đánh dấu như vậy. – rbaleksandar

1

Tôi không tin rằng chuỗi mới tồn tại cho đến khi chạy được gọi. Vì vậy, hàm tạo là một luồng khác với run(). Điều gì xảy ra nếu bạn di chuyển việc tạo đối tượng người quản lý từ hàm tạo để chạy()? Tôi tưởng tượng rằng sẽ sửa lỗi đầu tiên, nếu không phải là lỗi hẹn giờ là tốt.

Ngoài ra, tôi nghĩ nhiều người vẫn đang xây dựng chủ đề theo cách của bạn, nhưng bạn có thể muốn xem this.

+0

Không hoạt động, tôi cũng đã xem bài đăng trên blog đó. Kiểm tra câu hỏi của tôi. – Ahmed

+0

Không hoạt động? Di chuyển việc tạo ra người quản lý ra khỏi constructor, hoặc sử dụng moveToThread theo cách mà liên kết thảo luận? Tôi không nghĩ rằng chỉnh sửa của bạn là chính xác. Nếu bạn sử dụng Citizen làm đối tượng được chuyển đến QThread, Citizen không nên lấy từ QThread. –

+0

trước tiên, QThread của bạn, được tạo trên một ngăn xếp, phá hủy sau khi bạn thoát khỏi chương trình con, bắt đầu chuỗi. Bạn không muốn điều này, tôi giả sử. Bạn nên đặt QThread thành viên của một lớp. Sau đó, bạn không cần gọi phương thức 'thoát' một cách rõ ràng. Chủ đề của bạn sẽ dừng khi trả về 'exec'. – Lol4t0

1

Bạn cần xem xét thread affinity. Thông báo lỗi đó không nói dối hoặc mất trí, thông báo cho bạn biết chính xác là gì.

+0

Vui lòng kiểm tra lại câu hỏi của tôi, tôi đã chỉnh sửa nó ... – Ahmed

+0

liên kết của bạn đã chết. – Akiva

1

Sự cố của bạn chủ yếu là do cố gắng phân lớp QThread. Mặc dù tài liệu giới thiệu nó, nó không phải là cách tốt nhất để sử dụng QThread. Vui lòng xem this question and answer để biết thêm thông tin và liên kết.

1

Tôi chưa tìm ra lỗi startTimers mặc dù nó có thể liên quan đến lỗi đầu tiên. Trong mọi trường hợp, bạn sẽ có thể sửa lỗi đầu tiên. Tôi đã chạy vào vấn đề này trong Qt một vài lần và tôi thấy điều này là "tốt nhất" cách để làm việc xung quanh nó là tạo ra một chức năng khởi tạo và một chức năng cleanUp. Tất cả các thành viên của lớp là con trỏ được khởi tạo cho NULL cho đến khi chạy được gọi. Lưu ý rằng "tốt nhất" là trong dấu ngoặc kép vì có chắc chắn sẽ có ý kiến ​​khác nhau nhưng nó hoạt động cho hầu hết các tình huống cho tôi.

Tiêu đề

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

Thực hiện

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. 

Bạn có được điều này bởi vì bạn đang tạo các QNetworkAccessmanager trong constructor của Citizen, hiện đang được gọi là trong chủ đề "gốc". Khi đối tượng Citizen được chuyển sang chủ đề mới, QNetworkAccessmanager vẫn có mối quan hệ luồng được đặt thành luồng ban đầu nhưng trong cuộc gọi chạy nó sẽ cố gắng tạo đối tượng QNetworkReply (và có thể là các đối tượng khác) trong chuỗi mới. Mà tạo cảnh báo ở trên.

Nếu bạn tạo người quản lý trong khe chạy (hoặc tại bất kỳ thời điểm nào sau khi đối tượng Công dân được chuyển sang chuỗi mới) sẽ không xảy ra.

Tuy nhiên, bạn vẫn gặp một số vấn đề. Ví dụ, lớp Citizen thực sự không cần phải là một QThread. Nó không cần thiết làm phức tạp nó. Nó sẽ đủ cho mục đích của bạn (afaict) để phân lớp một QObject. Chỉ cần tạo một khe bình thường và kết nối nó với tín hiệu QThread :: started(). Và như OrcunC chỉ ra bạn cần phải chắc chắn rằng các QThread dụ là đúng scoped.

Để biết thêm về luồng: http://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/

Ví dụ:

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"); 
Các vấn đề liên quan