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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: QObject: Cannot create children for a parent that is in a different thread.  (Прочитано 11652 раз)
Alex_C
Гость
« : Сентябрь 08, 2012, 18:04 »

Делаю QTcpServer. Возникает ошибка
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNativeSocketEngine(0x3ef148), parent's thread is CatTcpThread(0x3ea288), current thread is QThread(0x3e5250)

Приведу код:

h-фаил
Код
C++ (Qt)
class CatTcpThread : public QThread
{
   Q_OBJECT
public:
   CatTcpThread(int socketDescription, QObject *parent=0);
   void run();
   QHostAddress GetAddressClient();
 
private:
   int m_socketDescriptor;
   QTcpSocket *m_client;
   QHostAddress m_addr;
 
public slots:
   void slot_onRead();
   void slot_onDisconnect();
   void slot_onGetError(QAbstractSocket::SocketError error);
   void slot_transmiteDataForClient(QByteArray &);
 
signals:
   void signal_setup();
   void signal_startCat(int);
   void signal_endWork();
   void signal_error(QAbstractSocket::SocketError);
};
 
//******************************************************************************
// CatTcpServer
//******************************************************************************
 
class CatTcpServer : public QTcpServer
{
   Q_OBJECT
public:
   CatTcpServer(const QString &iniFileName, QObject *parent = 0);
   ~CatTcpServer();
   bool startServer();
   void writeData(QByteArray &block, QString ip);
   void writeData(QByteArray &block);
 
protected:
   void removeAllThreads();
 
   void incomingConnection(int socketDescriptor);
   QList<CatTcpThread*> m_threads;
 
public slots:
   void slot_removeThread();
   void slot_setup();
   void slot_startCat(int);
   void slot_endWork();
 
signals:
   void signal_transmiteDataForClient(QByteArray &);
private:
   ...
 
};
 

cpp-файл
Код
C++ (Qt)
void CatTcpThread::run()
{
   QByteArray block;
   m_client = new QTcpSocket();
   QHostAddress local;
 
   if(!m_client->setSocketDescriptor(m_socketDescriptor))
   {
       return;
   }
 
   connect(m_client, SIGNAL(readyRead()),
           this, SLOT(slot_onRead()), Qt::DirectConnection);
   connect(m_client, SIGNAL(disconnected()),
           this, SLOT(slot_onDisconnect()), Qt::DirectConnection);
   connect(m_client, SIGNAL(error(QAbstractSocket::SocketError)),
           this, SLOT(slot_onGetError(QAbstractSocket::SocketError)),
           Qt::DirectConnection);
 
   m_addr = m_client->peerAddress();
 
   exec();
}
 
// Отправить данные клиенту
void CatTcpThread::slot_transmiteDataForClient(QByteArray &block)
{
   if(block.size() > 0)
   {
       if(m_client->write(block) < 0)
           return;
 
       if(!m_client->waitForBytesWritten(3000))
           return;
   }
}
 
//******************************************************************************
// CatTcpServer
//******************************************************************************
 
void CatTcpServer::writeData(QByteArray &block)
{
//    emit signal_transmiteDataForClient(block);
   for(int i = 0; i < m_threads.count(); i++)
   {
       // Ошибка возникает вот тут!!!
       m_threads[i]->slot_transmiteDataForClient(block);
   }
}
 
 

Т.е. ошибка в ф-ции void CatTcpThread::slot_transmiteDataForClient(QByteArray &block), а точнее вот тут
m_client->write(block)
как я понимаю, это из-за того, что m_client создается в другом потоке. Вопрос - как правильно переделать  CatTcpThread? Создавать m_client не в run()?



Записан
Alex_C
Гость
« Ответ #1 : Сентябрь 08, 2012, 18:19 »

В общем проблему решил сам

Код
C++ (Qt)
void CatTcpServer::incomingConnection(int socketDescriptor)
{
   qDebug() << "Incomming connection!";
 
   CatTcpThread *thread = new CatTcpThread(socketDescriptor, this);
   connect(thread, SIGNAL(finished()), this, SLOT(slot_removeThread()),
           Qt::DirectConnection);
   connect(thread, SIGNAL(signal_setup()), this, SLOT(slot_setup()));
   connect(thread, SIGNAL(signal_startCat(int)), this, SLOT(slot_startCat(int)));
   connect(thread, SIGNAL(signal_endWork()), this, SLOT(slot_endWork()));
   connect(this, SIGNAL(signal_transmiteDataForClient(QByteArray &)),
           thread, SLOT(slot_transmiteDataForClient(QByteArray&)));
   m_threads.append(thread);
 
   this->moveToThread(thread);  <- Вот эту строку добавил
   thread->start();
}
 

Решение нашел в интернете. А теперь подскажите (не до конца понял), что делает moveToThread?
Записан
mutineer
Гость
« Ответ #2 : Сентябрь 08, 2012, 19:16 »

Перемещает наследника QObject в другой тред, то есть объект теперь работает не с главным QEventLoop, а с QEventLoop треда
Записан
Alex_C
Гость
« Ответ #3 : Сентябрь 08, 2012, 21:39 »

Спасибо за ответ!
Но выходит я не правильно сделал.
this->moveToThread(thread);
работает, только если один клиент подключен. При подключении второго клиента, он начинает работать, что верно - теперь же для него
this->moveToThread(thread);
а первый выдает ошибку.
А как выйти из этого положения?
Записан
V1KT0P
Гость
« Ответ #4 : Сентябрь 08, 2012, 21:59 »

Спасибо за ответ!
Но выходит я не правильно сделал.
this->moveToThread(thread);
работает, только если один клиент подключен. При подключении второго клиента, он начинает работать, что верно - теперь же для него
this->moveToThread(thread);
а первый выдает ошибку.
А как выйти из этого положения?
Если я правильно понял, то сперва у тебя была ошибка из-за того что ты лез в объект принадлежащий чужому треду. Ты решил это переносом сервера в тред в котором находится этот объект. Тут ты создаешь еще один объект и снова начинаешь переносить сервер в уже друго тред(это уже начинает плохо попахивать). И что же ты получаешь, ведь теперь при обращении к первому объекту получится таже ситуация что и до перемещения треда ибо сервер уже работает в треде другого объекта.
Зачем ты делаешь это:
Код
C++ (Qt)
m_threads[i]->slot_transmiteDataForClient(block);
Если первоначально ты делал правильно:
Код
C++ (Qt)
emit signal_transmiteDataForClient(block);
Ибо если объект принадлежит треду то и вызывать его лучше из этого треда. А постоянно перемещать сервер между разными тредами я считаю не такой уж хорошей идеей.
Записан
Alex_C
Гость
« Ответ #5 : Сентябрь 08, 2012, 22:26 »

Без разницы. Что emit, что циклом проходить по всем тредам - результат один и тот же.
Пока понять не могу следующее - проблема возникает тут

Код
C++ (Qt)
void CatTcpThread::slot_transmiteDataForClient(QByteArray block)
{
   if(block.size() > 0)
   {
       if(m_client->write(block) < 0)  <- вот тут возникает эта ошибка
           return;
       if(!m_client->waitForBytesWritten(3000))
           return;
   }
}
 

Запустил три клиента - сервер выдал на каждого клиента по одной такой ошибке... и прекрасно работал! Это как то странно...


И за одно еще одна не понятка. У меня
Код
C++ (Qt)
void CatTcpServer::incomingConnection(int socketDescriptor)
{
   qDebug() << "Incomming connection!";
 
   CatTcpThread *thread = new CatTcpThread(socketDescriptor, this);
   connect(thread, SIGNAL(finished()), this, SLOT(slot_removeThread()),
           Qt::DirectConnection);
   connect(thread, SIGNAL(signal_setup()), this, SLOT(slot_setup()));
   connect(thread, SIGNAL(signal_startCat(int)), this, SLOT(slot_startCat(int)));
   connect(thread, SIGNAL(signal_endWork()), this, SLOT(slot_endWork()));
   connect(this, SIGNAL(signal_transmiteDataForClient(QByteArray)),
           thread, SLOT(slot_transmiteDataForClient(QByteArray)));
   m_threads.append(thread);
 
   thread->start();
}
 
void CatTcpServer::slot_removeThread()
{
   qDebug() << "Remove thread";
   CatTcpThread *thread = qobject_cast<CatTcpThread*>(sender());
   m_threads.removeAt(m_threads.indexOf(thread));
   thread->deleteLater();
}
 

когда я закрываю клиента (просто выхожу из программы), как серверу узнать, что клиента больше нет?
Записан
Alex_C
Гость
« Ответ #6 : Сентябрь 08, 2012, 22:27 »

Да и конечно moveToThread была совсем не правильной идеей.
Записан
V1KT0P
Гость
« Ответ #7 : Сентябрь 08, 2012, 22:40 »

Ну смотри вот ошибка:
Цитировать
Parent is QNativeSocketEngine(0x3ef148), parent's thread is CatTcpThread(0x3ea288), current thread is QThread(0x3e5250)
В ней сказано что QNativeSocketEngine принадлежит потоку CatTcpThread(указатель на 0x3ea288), но вызывается из другого потока указатель которого 0x3e5250. То-есть ты вызываешь метод объекта не из того потока к которому он принадлежит.
Вызывай методы CatTcpThread только через сигналы.
Для того чтоб серверу знать какой клиент разорвал связь:
1) Создаешь у CatTcpServer слот который будет вызываться когда CatTcpThread разрывает связь. У CatTcpThread для этого делаешь сигнал.
2) Соединяешь сигнал CatTcpThread с слотом CatTcpServer через QSignalMapper, для того чтоб знать какой именно CatTcpThread вызвал этот слот.
Записан
Alex_C
Гость
« Ответ #8 : Сентябрь 08, 2012, 22:48 »

Так я и вызываю через сигналы...
Нет, тут все не так просто. Я уже прогуглил интернет - аналогичных вопросов много, ответов - нет.
Можно конечно отказаться от использования отдельных тредов для каждого соединения.
Такое впечатление, что пример threadedfortuneserver из Qt только и работает так, как он там и сделан...
Записан
V1KT0P
Гость
« Ответ #9 : Сентябрь 08, 2012, 22:53 »

Так я и вызываю через сигналы...
Нет, тут все не так просто. Я уже прогуглил интернет - аналогичных вопросов много, ответов - нет.
Можно конечно отказаться от использования отдельных тредов для каждого соединения.
Такое впечатление, что пример threadedfortuneserver из Qt только и работает так, как он там и сделан...

Дело в том, что я сам лично делал сервер в котором каждый клиент обрабатывался в своем потоке и все работало как часы.
Но я всегда на всякий случай при коннекте указываю Qt::QueuedConnection. Попробуй и ты, может коннекты неправильно соединяются.
Записан
Alex_C
Гость
« Ответ #10 : Сентябрь 08, 2012, 23:35 »

Да конечно Qt::QueuedConnection пробовал в первую очередь.

В общем так - переделал без отдельных тредов

Код
C++ (Qt)
void CatTcpServer::incomingConnection(int socketDescriptor)
{
   //    CatTcpThread *thread = new CatTcpThread(socketDescriptor, this);
   //    connect(thread, SIGNAL(finished()), this, SLOT(slot_removeThread()),
   //            Qt::DirectConnection);
   //    connect(thread, SIGNAL(signal_setup()), this, SLOT(slot_setup()));
   //    connect(thread, SIGNAL(signal_startCat(int)), this, SLOT(slot_startCat(int)));
   //    connect(thread, SIGNAL(signal_endWork()), this, SLOT(slot_endWork()));
   //    connect(this, SIGNAL(signal_transmiteDataForClient(QByteArray)),
   //            thread, SLOT(slot_transmiteDataForClient(QByteArray)));
   //    m_threads.append(thread);
 
   //    thread->start();
 
   QTcpSocket *clientConnection = new QTcpSocket();
   clientConnection->setSocketDescriptor(socketDescriptor);
 
   qDebug() << "Incomming connection!";
   clientConnections.append(clientConnection);
   connect(clientConnection, SIGNAL(readyRead()),
           this, SLOT(slot_onRead()), Qt::DirectConnection);
   connect(clientConnection, SIGNAL(disconnected()),
           this, SLOT(slot_onDisconnect()));
}
 

То, что закомментированно - как было. Без тредов работает на ура.
Опять же по гуглу - все ответы на подобные вопросы сводились к этому же - делать без отдельных тредов. На сколько я понимаю, проблема здесь в том, что QTcpSocked изначально использует свою тред.  Но все же очень был бы раз понять, почему с тредами не работает, если даже сами разработчики пример с тредом приводят. Хотя конечно тот пример , что у них - принципиально другой.
Записан
Alex_C
Гость
« Ответ #11 : Сентябрь 09, 2012, 00:24 »

Ответ найден тут
http://www.prog.org.ru/topic_9349_0.html
- ключевое слово
thread->moveToThread(thread);
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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