Russian Qt Forum
Ноябрь 23, 2024, 02:55 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: QNAM+Многопоточность  (Прочитано 6847 раз)
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 не хочет работать в другом потоке?
Записан
Bepec
Гость
« Ответ #1 : Октябрь 02, 2013, 17:51 »

Ну что сказать. У вас созданный QNAM и вы его пытаетесь использовать в другом потоке.
Я лично приверженец наследования от QThread и эту проблема просто игнорирую.

PS чего и вам советую, но что-то у вас там с потоками напутано.
Записан
carrygun
Гость
« Ответ #2 : Октябрь 02, 2013, 17:56 »

Но я его создаю в том же потоке, в котором и испльзую.
Записан
Bepec
Гость
« Ответ #3 : Октябрь 02, 2013, 18:33 »

breakpoint и thread Id смотрите. Локализуйте, где вообще происходит и в том же потоке, али же в ином?
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #4 : Октябрь 02, 2013, 18:46 »

Здесь я создаю объект QNAM, если его еще нет, и связываю сигнал со слотом. В конструкторе класса accessManager зануляется и создается именно в слоте, так как moveToThread будет выполнен после создания объекта.
Что значит если его еще нет? А откуда он может взяться или вы один объект worker используете в нескольких нитках?
Записан
carrygun
Гость
« Ответ #5 : Октябрь 02, 2013, 19:35 »

Я несколько раз использую метод doWork() у объекта. И если в первый вызов мне надо создать QNAM, то в последующие вызовы объект "поидее" должен уже существовать.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #6 : Октябрь 02, 2013, 19:45 »

Я несколько раз использую метод doWork() у объекта.
А в контексте какой нити этот объект используется в дальнейшем?
Вот вы показали как этот объект вызывается в первый раз, а следующий раз он как вызывается, еще раз запускается другая нить?
Записан
carrygun
Гость
« Ответ #7 : Октябрь 02, 2013, 20:25 »

При создании объекта связываю его сигналами в главном потоке, потом при "поимке" сигнала преобразую sender() в ClassName и уже дергаю метод. Собака тут зарыта?
« Последнее редактирование: Октябрь 02, 2013, 21:04 от carrygun » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #8 : Октябрь 02, 2013, 21:21 »

и уже дергаю метод.
То есть из контекста главного потока вызываете этот метод?
Тогда этот метод будет выполняться в контексте главной нитки, а QNAM создан в контексте рабочей нитки, вот и причина.
Записан
carrygun
Гость
« Ответ #9 : Октябрь 02, 2013, 21:25 »

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

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

Сообщений: 4350



Просмотр профиля
« Ответ #10 : Октябрь 02, 2013, 21:31 »

И как поступить лучше? Как мне имея указатель на QThread "заставить" выполнить doWork() объекту, находящемуся в том треде?
Не вызывать метод doWork из главного потока.
Покажите код, как это происходит (последующие вызовы метода doWork), тогда может станет понятней, чего вы хотите добиться.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #11 : Октябрь 02, 2013, 21:34 »

И я бы parent менеджеру устанавливал, иначе он не будет перемещен в контекст рабочей нитки:
Код
C++ (Qt)
void ClassName::doWork()
{
...
       accessManager = new QNetworkAccessManager( this );
}
 
Записан
carrygun
Гость
« Ответ #12 : Октябрь 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();
   }
}
 
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #13 : Октябрь 02, 2013, 22:42 »

В слоте обрабатываю некоторые моменты, примерный код без лишнего:
Из главной нитки не нужно вызывать doWork напрямую, лучше воспользоваться сигнально-слотовым механизмом.
Или посылать сигнал объекту, который соединить с doWork или вызывать doWork через очередь сообщений используя QMetaObject::invokeMethod.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.132 секунд. Запросов: 23.