Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: carrygun от Октябрь 02, 2013, 17:36



Название: QNAM+Многопоточность
Отправлено: carrygun от Октябрь 02, 2013, 17:36
Всем привет.

Пишу сейчас один проект в котором используется работа с сетью и чтобы ускорить работу было решено работу с сетью сделать многопоточной. И тут я столкнулся с проблемой, которую не могу понять как решить, да и вообще что не так. Форум полистал, погуглил и решения либо не помогают либо совсем не о том. В общем к делу.

Для начала я сделал класс, унаследовавшись от QObject. Помимо основных методов для работы с сетьи и прочим есть слот doWork, небольшой кусок сюда скину:
Код
C++ (Qt)
void ClassName::doWork()
{
...
   if (!accessManager) {
       accessManager = new QNetworkAccessManager;
       connect(accessManager, SIGNAL(finished(QNetworkReply*)), SLOT(onManagerReply(QNetworkReply*)));
   }
...
   accessManager->get(...);
}
 
Здесь я создаю объект QNAM, если его еще нет, и связываю сигнал со слотом. В конструкторе класса accessManager зануляется и создается именно в слоте, так как moveToThread будет выполнен после создания объекта.
Теперь создание потоков и объектов в главном потоке. Опять же примерный кусок кода:
Код
C++ (Qt)
       ClassName *worker = new ClassName;
       QThread *workerThread = new QThread(this);
       //object params
...
       //signals/slots
       connect(worker, SIGNAL(workFinished()), workerThread, SLOT(quit()));
...
       //thread signals/slots
       connect(workerThread, SIGNAL(started()), worker, SLOT(doWork()));
       connect(workerThread, SIGNAL(finished()), SLOT(onWorkerThreadFinished()));
 
       //moving object to thread
       worker->moveToThread(workerThread);
       workerThread->start();
 

Поидее после запуска треда у, worker'a в этом треде должен сработать слот doWork, но когда созданный в данном слоте accessManager вызывает метод get, то в консоли "вывод приложения" появляется надпись:
Код:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0x125edf20), parent's thread is QThread(0x16acca68), current thread is QThread(0x125adce8)

Прошу знающих объяснить где и что я делаю неверно. Почему QNAM не хочет работать в другом потоке?


Название: Re: QNAM+Многопоточность
Отправлено: Bepec от Октябрь 02, 2013, 17:51
Ну что сказать. У вас созданный QNAM и вы его пытаетесь использовать в другом потоке.
Я лично приверженец наследования от QThread и эту проблема просто игнорирую.

PS чего и вам советую, но что-то у вас там с потоками напутано.


Название: Re: QNAM+Многопоточность
Отправлено: carrygun от Октябрь 02, 2013, 17:56
Но я его создаю в том же потоке, в котором и испльзую.


Название: Re: QNAM+Многопоточность
Отправлено: Bepec от Октябрь 02, 2013, 18:33
breakpoint и thread Id смотрите. Локализуйте, где вообще происходит и в том же потоке, али же в ином?


Название: Re: QNAM+Многопоточность
Отправлено: Old от Октябрь 02, 2013, 18:46
Здесь я создаю объект QNAM, если его еще нет, и связываю сигнал со слотом. В конструкторе класса accessManager зануляется и создается именно в слоте, так как moveToThread будет выполнен после создания объекта.
Что значит если его еще нет? А откуда он может взяться или вы один объект worker используете в нескольких нитках?


Название: Re: QNAM+Многопоточность
Отправлено: carrygun от Октябрь 02, 2013, 19:35
Я несколько раз использую метод doWork() у объекта. И если в первый вызов мне надо создать QNAM, то в последующие вызовы объект "поидее" должен уже существовать.


Название: Re: QNAM+Многопоточность
Отправлено: Old от Октябрь 02, 2013, 19:45
Я несколько раз использую метод doWork() у объекта.
А в контексте какой нити этот объект используется в дальнейшем?
Вот вы показали как этот объект вызывается в первый раз, а следующий раз он как вызывается, еще раз запускается другая нить?


Название: Re: QNAM+Многопоточность
Отправлено: carrygun от Октябрь 02, 2013, 20:25
При создании объекта связываю его сигналами в главном потоке, потом при "поимке" сигнала преобразую sender() в ClassName и уже дергаю метод. Собака тут зарыта?


Название: Re: QNAM+Многопоточность
Отправлено: Old от Октябрь 02, 2013, 21:21
и уже дергаю метод.
То есть из контекста главного потока вызываете этот метод?
Тогда этот метод будет выполняться в контексте главной нитки, а QNAM создан в контексте рабочей нитки, вот и причина.


Название: Re: QNAM+Многопоточность
Отправлено: carrygun от Октябрь 02, 2013, 21:25
и уже дергаю метод.
То есть из контекста главного потока вызываете этот метод?
Тогда этот метод будет выполняться в контексте главной нитки, а QNAM создан в контексте рабочей нитки, вот и причина.

И как поступить лучше? Как мне имея указатель на QThread "заставить" выполнить doWork() объекту, находящемуся в том треде?


Название: Re: QNAM+Многопоточность
Отправлено: Old от Октябрь 02, 2013, 21:31
И как поступить лучше? Как мне имея указатель на QThread "заставить" выполнить doWork() объекту, находящемуся в том треде?
Не вызывать метод doWork из главного потока.
Покажите код, как это происходит (последующие вызовы метода doWork), тогда может станет понятней, чего вы хотите добиться.


Название: Re: QNAM+Многопоточность
Отправлено: Old от Октябрь 02, 2013, 21:34
И я бы parent менеджеру устанавливал, иначе он не будет перемещен в контекст рабочей нитки:
Код
C++ (Qt)
void ClassName::doWork()
{
...
       accessManager = new QNetworkAccessManager( this );
}
 


Название: Re: QNAM+Многопоточность
Отправлено: carrygun от Октябрь 02, 2013, 21:49
При создании объекта я делаю коннект, один из них:
Код
C++ (Qt)
connect(worker, SIGNAL(userScanned(...)), SLOT(onWorkerUserScanned(...)));
 

В слоте обрабатываю некоторые моменты, примерный код без лишнего:
Код
C++ (Qt)
void SearchWidget::onWorkerUserScanned(...)
{
   ClassName *w = qobject_cast<ClassName *>(sender());
   if (!w)
       return;
...
   if (...) {
       w->setSomething(...);
       w->doWork();
   } else {
       ...
       w->stopWork();
   }
}
 


Название: Re: QNAM+Многопоточность
Отправлено: Old от Октябрь 02, 2013, 22:42
В слоте обрабатываю некоторые моменты, примерный код без лишнего:
Из главной нитки не нужно вызывать doWork напрямую, лучше воспользоваться сигнально-слотовым механизмом.
Или посылать сигнал объекту, который соединить с doWork или вызывать doWork через очередь сообщений используя QMetaObject::invokeMethod.