Qt Передача "this" в качестве родителя при создании QObject, который перемещается в поток - PullRequest
4 голосов
/ 29 февраля 2012

После прочтения документации я наткнулся на это:

Дочерний элемент QObject всегда должен создаваться в потоке, в котором был создан родительский элемент.Это подразумевает, среди прочего, что вы никогда не должны передавать объект QThread (this) как родительский объект для объекта, созданного в потоке (поскольку сам объект QThread был создан в другом потоке).

Я не совсем уверен, что это значит, поэтому я сделал несколько примеров и подумал, где это применимо.

A.

class MyThread : public QThread {
    MyThread(QObject *parent = 0) : QThread(parent) {
        QNetworkAccessManager *test = new QNetworkAccessManager (this); 
       // here I assume that this child(test) will have the main thread (creator of MyThread)
       // as it's parent and it will go horrible wrong. 
    }
    ....
}

B.

class MyThread : public QThread {
    QNetworkAccessManager *test;
    MyThread(QObject *parent = 0) : QThread(parent) {}

    void run {
       test = new QNetworkAccessManager(this); 
       // will the main thread be the parent here aswell?
       // if I remove "this" this will work since its created in the thread (not the main thread)
    }
    ....
}

Окей, а как насчет этого?

Обратите внимание, что эти объекты будут созданы и запущены следующим образом:

MyThread *fakeThread = new MyThread; // should I or should I not pass "this" as a parent here?
QThread realThread;

fakeThread->moveToThread(&realThread);

realThread.start();
QMetaObject::invokeMethod(fakeThread, "start", Qt::QueuedConnection);

C.

class MyThread : public QObject {
    MyThread(QObject *parent = 0) : QObject(parent) {
        QNetworkAccessManager *test = new QNetworkAccessManager(this); 
       // I assume this constructor still will be create the the QNAM as a child to 
       // the main thread and it will not work
    }

    void start {
        ....
    }
}

D.

class MyThread : public QObject {
    QNetworkAccessManager *test;
    MyThread(QObject *parent = 0) : QObject(parent) {}

    void start {
       test = new QNetworkAccessManager(this); 
       // What about now? 
    }
    ....
}

Я действительно хочу реализовать опцию D, чтобы у меня было множество объектов, которые можно легко удалить.Например, если я хочу прочитать большое количество файлов, и я хочу, чтобы все они были перераспределены, когда поток умирает.

Кроме того, в чем главное отличие производной версии QThread от той, в которой вы перемещаете QObject в поток?В документации всегда указывается, что нужно делать, а что нет при работе с Qthread, но я немного не уверен в этом.

Моя самая большая проблема сейчас в том, что у меня есть класс, производный от QObject, который будет перемещен в поток.В этом объекте я создаю другой объект.Этот объект содержит QNetworkAccessManager и QFile.Куда бы я передал «это» как родитель, или я вообще не должен этого делать, находясь в потоке?Пожалуйста, объясни.

В данный момент в куче создается объект (назовем его RestHandler), принадлежащий объекту (uploader, который является потоком).Это потому, что большинство функций использует его, и если он создан в заголовочном файле в стеке, его родитель будет основным потоком.В RestHandler a QNetworkAccessManager будет создан в стеке.

Редактировать:

После прочтения ответов я попытался создать в стеке restHandler и QNetworkAccessManager, а затем переместить их.Вывод был следующим:

Resthandeler конструктор: restHandler создан в потоке QThread (0x22d5e8) с accessManager в потоке QThread (0x22d5e8) текущее местоположение QThread (0x22d5e8)

Затем, когда я пытаюсь переместить resthandler, я получаю следующее предупреждение:

QObject::moveToThread: Current thread (0x1fbf0e0) is not the object's thread (0x22d5e8).
Cannot move to target thread (0x1fbf0e0)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0x42a477c), parent's thread is QThread(0x22d5e8), current thread is QThread(0x4196f98)

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

1 Ответ

3 голосов
/ 29 февраля 2012

Из документации Threads и QObjects :

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

Для вас это означает, что ваш QNetworkAccessManager должен находиться в потоке, где вы будете подключать сокеты и т. Д. Он не может быть дочерним по отношению к вашему MyThread объекту, и если он инициализирован в вашем конструкторе, он должен быть перемещенным на MyThread после начала потока.

Вы должны убедиться, что все объекты, созданные в потоке, удалены прежде чем удалить QThread. Это можно легко сделать, создав объекты в стеке в вашей реализации run ().

Есть ли причина, по которой вы выделяете QNetworkAccessManager в куче? Можете ли вы вместо этого быть простым членом (в стеке)?

Из документов QNetworkAccessManager:

API доступа к сети построен вокруг одного QNetworkAccessManager объект , который содержит общую конфигурацию и настройки для запросы он отправляет. Он содержит настройки прокси и кеша, так как а также сигналы, связанные с такими вопросами, и ответные сигналы, которые могут использоваться для мониторинга хода работы сети. One QNetworkAccessManager должно быть достаточно для всего приложения Qt.

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

...