Невозможно использовать QtNetwork, поскольку приложение использует разные потоки

В моем приложении возникло странное поведение, когда я использую QtNetwork. Я могу легко создать экземпляры QTcpSever и QTcpSocket, и все работает нормально, но когда дело доходит до QTcpSocket::write(), возникает следующая ошибка:

Ошибка

QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNativeSocketEngine(0x7f66980022e0), parent's thread is QThread(0x7f66a0020be0), current thread is QThread(0x7f66a0020e20)
QSocketNotifier: Can only be used with threads started with QThread

Что мне странно: я понятия не имею, что/где это QThread(0x7f66a0020e20) и как на него повлиять (посмотрите отладку ниже)

Программа

Я расширяю свое основное приложение (библиотеку) поддержкой сети. Я помещаю сетевые службы в дополнительный класс.

вот выдержка из основного приложения/библиотеки, где создается моя сетевая поддержка:

QThread *thread = new QThread;
wifi = new WirelessNet(0, thread);
wifi->moveToThread(thread);
connect(thread,SIGNAL(started()), wifi,SLOT(initWifi()));
thread->start();

расширение сетевого класса:

WirelessNet::WirelessNet(QObject *parent, QThread *comThread): QTcpServer(parent)
{
     clientThread = comThread;
}

void WirelessNet::initWifi()
{
    listen(QHostAddress::Any, 5220);
    connect(this,SIGNAL(newConnection()),this,SLOT(connectionRequest()));
}

void WirelessNet::connectionRequest()
{
    client = this->nextPendingConnection();
    if(client)
        connect(client, SIGNAL(readyRead()), this, SLOT(receiveMessage()));
}

void WirelessNet:sendData(QByteArray msg)
{
if (client)
{
    qDebug()<<"FIRST "<< client->thread() << " - " << this->thread() << "\n";
    client->write(msg);
    client->waitForBytesWritten();
    qDebug()<<"LAST " << client->thread() << " - " << this->thread() << "\n";
}
}

(client и clientThread являются членами класса: QTcpSocket*, QThread* соответственно)

Отладка

Вот что выводит консоль, когда дело доходит до части sendData():

FIRST QThread(0x7f66a0020be0) - QThread(0x7f66a0020be0)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNativeSocketEngine(0x7f66980022e0), parent's thread is QThread(0x7f66a0020be0), current thread is QThread(0x7f66a0020e20)
QSocketNotifier: Can only be used with threads started with QThread
LAST QThread(0x7f66a0020be0) - QThread(0x7f66a0020be0)

Заключение

Другими словами, я понятия не имею, к какому объекту я должен применить moveToThread(). Я уже пробовал client->moveToThread(clientThread), а также this->moveToThread(clientThread). К сожалению, я не вижу дополнительных объектов для проверки.

У кого-нибудь есть идея?


person user3085931    schedule 31.03.2016    source источник
comment
Я не понимаю, как/где настраивается clientThread. У вас есть в конструкторе clientThread = comThread;, но затем, когда вы создаете WirelessNet, вы передаете 0 для родителя, что хорошо, но вы не передаете второй аргумент для потока...: wifi= new WirelessNet(0);. Зачем вам нужно проходить в потоке? - вы можете просто создать его по мере необходимости в WirelessNet (возможно, иметь его как переменную-член), например. QThread *m_ClientThread, а затем m_ClientThread = new QThread; Теперь у вас есть новый поток, и все он инкапсулирован в WirelessNet (и вы можете использовать moveToThread...).   -  person code_fodder    schedule 31.03.2016
comment
@code_fodder извините, это была опечатка. Обновил верхний пост (основное приложение). Я передаю поток, чтобы я мог moveToThread() в требуемый родительский поток. Дело в том, что все (я знаю) уже настроено на родительский поток, но этот неясный объект, который я ищу - я надеялся, что это стало ясно из части сообщения отладка.   -  person user3085931    schedule 31.03.2016
comment
Вы случайно не вызываете WirelessNet:sendData непосредственно из основного потока, не так ли?   -  person thuga    schedule 31.03.2016
comment
@thuga Да. Вы правы, я должен создать дополнительный слот для отправки   -  person user3085931    schedule 31.03.2016
comment
@thuga теперь работает, если вы дадите ответ, я отмечу его как решенный   -  person user3085931    schedule 31.03.2016
comment
В этом вопросе недостаточно кода для воспроизведения проблемы, поэтому он не по теме. В будущем, задавая такие вопросы, создайте минимальный автономный (один main.cpp) тестовый пример. См., например. этот ответ для примера того, как должен выглядеть код в вашем вопросе.   -  person Kuba hasn't forgotten Monica    schedule 31.03.2016
comment
Это не проблема, как указал туга и ответил, что вы вызвали направление функции из другого потока +1 к этому ответу, но просто хотели прояснить еще одну вещь. Вы передаете указатель потока в WirelessNet потока, в который он перемещен, и сохраняете его в clientThread. Вам не нужно, вы можете получить доступ к вашему текущему потоку с помощью QThread::currentThread() - возвращает QThread *, но что вы делаете с этим указателем QThread? - почему-то мне кажется, что что-то еще может быть не так :o   -  person code_fodder    schedule 31.03.2016
comment
@code_fodder теперь все ясно. Моя исходная версия все равно не использовала его, я передал QThread * только для отладки, поэтому я не смешиваю потоки, как хотел Qt. Я уже удалил их снова.   -  person user3085931    schedule 31.03.2016


Ответы (2)


Кажется, вы вызываете WirelessNet:sendData непосредственно из основного потока. Это приводит к тому, что все внутри этой функции также выполняется в основном потоке. Ваш client живет в новом потоке и не является потокобезопасным. Он пытается создать дочерние элементы, но текущий поток отличается от потока, в котором живет client. Вот почему вы получаете это сообщение об ошибке.

Вы можете исправить это, просто сделав WirelessNet:sendData слотом и вызывая его через сигнал из основного потока.

person thuga    schedule 31.03.2016

Я предполагаю, что конструктор вашего класса вызывается в вызывающем потоке, а сам поток выполняется в методе run() вашего класса. Решение состоит в том, чтобы инициализировать QTcpServer в начале вашего метода run(), чтобы инициализация и связь через этот класс выполнялись в одном и том же потоке.

person IceFire    schedule 31.03.2016
comment
Я попытался с дополнительным членом QTcpServer*, инициализировал в initWifi() и переместил на clientThread, все настроил на этот member-сервер, расширил qDebug() потоком этого сервера, который теперь такой же, как у всех еще - но ошибка постоянна - person user3085931; 31.03.2016
comment
Вы можете проверить с помощью QThread::currentThread(), где выполняется код. Я думаю, что initWifi снова вызывается исходным потоком. Я бы действительно попытался поместить это в начало вашего метода run - person IceFire; 31.03.2016
comment
Я не уверен, что run() вы имеете в виду. Класс WirelessNet наследуется от QTcpServer, я не реализовал run() и не выполняю wifi->start(). Однако, поскольку сначала вызывается initWifi, когда wifi уже перемещен в новый поток, в старом больше ничего не должно создаваться, насколько я понял - person user3085931; 31.03.2016
comment
Хорошо, забудьте о run. Однако вы проверили, с каким потоком initWifi вызывается? Вы правы в том, что раньше он перемещался в новый поток, но какое-то несоответствие потоков, похоже, сохраняется. - person IceFire; 31.03.2016
comment
спасибо за вашу помощь, как указал thuga, я пропустил связь сигнала/слота для отправки, я вызывал эту функцию непосредственно из main. теперь это работает - person user3085931; 31.03.2016