Russian Qt Forum

Qt => Работа с сетью => Тема начата: Luck от Сентябрь 10, 2010, 15:03



Название: Многопоточное использование QUdpSocket
Отправлено: Luck от Сентябрь 10, 2010, 15:03
Здравствуйте.
Возникла сложность при использовании QUdpSocket.
В приложении 3 потока:
1) поток, в котором выполняется функция main;
2) поток (унаследован от QThread), отправляющий данные через QUdpSocket;
3) поток (унаследован от QThread), принимающий данные через QUdpSocket.

При записи данных из потока main все замечательно: данные отправляются без ошибки, ответ приходит. При попытке отправки данных из 2го потока (даже если выполнять точно такие же действия, что в main-потоке) происходит ошибка (функция QUdpSocket::writeDatagram возвращает -1).

Действия выполняю следующие:

void CThread::f()
{
     QUdpSocket* udpSocket = new QUdpSocket();
     udpSocket->bind(QHostAddress::LocalHost, 7755);
     QByteArray ba = new QByteArray("asdf");
     udpSocket->writeDatagram(ba,QHostAddress::LocalHost,7756);     
}

С протоколом UDP я работаю первый раз, прошу сильно не ругаться ;) Подскажите, пожалуйста, в чем дело и как исправить?


Название: Re: Многопоточное использование QUdpSocket
Отправлено: Denjs от Сентябрь 10, 2010, 15:13
может быть свзяно с этим ?:
Цитировать
The QUdpSocket class provides a UDP socket.
...
Note: All functions in this class are reentrant.
Reentrancy and Thread-Safety:
Цитировать
Throughout the documentation, the terms reentrant and thread-safe are used to mark classes and functions to indicate how they can be used in multithread applications:

A thread-safe function can be called simultaneously from multiple threads, even when the invocations use shared data, because all references to the shared data are serialized.
A reentrant function can also be called simultaneously from multiple threads, but only if each invocation uses its own data.

Вы с одним объектом работаете из разных потоков? и как с данными?


Название: Re: Многопоточное использование QUdpSocket
Отправлено: BRE от Сентябрь 10, 2010, 15:14
Хотелось бы увидеть как выглядит весь класс CThread и как запускается этот поток.


Название: Re: Многопоточное использование QUdpSocket
Отправлено: Luck от Сентябрь 10, 2010, 15:22
То, что класс QUdpSocket - не ThreadSafe - это я знаю, но это имело бы значение, если бы я использовал один и тот же сокет в разных потоках или одни и те же данные. Однако, ошибка возникает даже при создании сокета и нахождении данных в экземпляре потока CThread.

Выложить полностью класс CThread возможности, к сожалению, нет.


Название: Re: Многопоточное использование QUdpSocket
Отправлено: BRE от Сентябрь 10, 2010, 15:27
Выложить полностью класс CThread возможности, к сожалению, нет.
Секретная разработка? ;D

Ладно, а откуда вызывается метод f()?


Название: Re: Многопоточное использование QUdpSocket
Отправлено: Luck от Сентябрь 10, 2010, 15:30
Метод f вызывается из метода run потока CThread


Название: Re: Многопоточное использование QUdpSocket
Отправлено: BRE от Сентябрь 10, 2010, 15:50
Метод f вызывается из метода run потока CThread
Наверно проще будет просто кусок кода.
Поток отправляет сообщение (переделал пример broadcastsender):
Код
C++ (Qt)
class Thread : public QThread
{
   Q_OBJECT
 
public:
   Thread();
 
protected:
   void run();
 
private slots:
   void broadcastDatagram();
 
private:
   QUdpSocket *udpSocket;
   int messageNo;
};
 

Код
C++ (Qt)
Thread::Thread()
   : QThread( 0 ), messageNo( 1 )
{
   moveToThread( this );
}
 
void Thread::run()
{
   udpSocket = new QUdpSocket;
 
   QTimer timer;
   connect( &timer, SIGNAL( timeout() ), SLOT( broadcastDatagram() ) );
   timer.start( 1000 );
 
   exec();
}
 
void Thread::broadcastDatagram()
{
   QByteArray datagram = "Broadcast message " + QByteArray::number( messageNo );
   udpSocket->writeDatagram(datagram.data(), datagram.size(),
                            QHostAddress::LocalHost, 45454);
   ++messageNo;
}
 


Название: Re: Многопоточное использование QUdpSocket
Отправлено: Luck от Сентябрь 10, 2010, 16:01
Думаю, проблема в том, что я не использовал метод moveToThread, спасибо.


Название: Re: Многопоточное использование QUdpSocket
Отправлено: Denjs от Сентябрь 10, 2010, 16:26
Код:
Thread::Thread()
    : QThread( 0 ), messageNo( 1 )
{
    moveToThread( this );
}

блин.. вот этот кусок меня радует ... отымей себя сам, простите за аналогии?
помещаем себя, в поток, который собою-же и олицетворяем)

разве не содержимое run() собственно будет выполняться в отдельном потоке? вне зависимости где создан родитель?

не, у меня аналогичное написано в конструкторе (вернее не совсем так: moveToThread( _parent->thread() ); - не помню зачем я это делал - видимо для того, что бы не ругалось на то, что родителя нельзя назначить из другого потока...

но не могли бы вы объяснить подробнее работу данного куска кода? зачем так? в ассистанте и примерах от троллей не нашел чего-то подобного....  ::)
заранее спасибо.


Название: Re: Многопоточное использование QUdpSocket
Отправлено: BRE от Сентябрь 10, 2010, 16:43
но не могли бы вы объяснить подробнее работу данного куска кода? зачем так? в ассистанте и примерах от троллей не нашел чего-то подобного....
Ну дык, смотрим.  :)
Сейчас этой строки нет.  :)

Код
C++ (Qt)
int main(...)
{
// Контекст первого потока
Thread *thread = new Thread; // Конструируем объект thread, он пренадлежит контексту первого потока.
}
 

Код
C++ (Qt)
void Thread::run()
{
// Появился контекст второго потока.
 
QTimer timer; // Объект timer принадлежит контексту второго потока.
 
// При соединении сигнала от объекта timer, который принадлежит второму потоку, со слотом объекта thread, который принадлежит первому потоку,
// будет выбран метод передачи сигналов через очередь событий, т.е. когда в потоке сработает таймер сообщение об этом будет помещено в очередь событий
// первого потока, соответсвенно слот будет отрабатывать из очереди первого потока.
// Нам это не нужно! Поэтому мы сообщаем Qt, что мы хотим считать объект thread принадлежащим контексту второго потока.
connect( &timer, SIGNAL( timeout() ), SLOT( broadcastDatagram() ) );
}
 


Название: Re: Многопоточное использование QUdpSocket
Отправлено: Denjs от Сентябрь 10, 2010, 18:01
но не могли бы вы объяснить подробнее работу данного куска кода? зачем так? в ассистанте и примерах от троллей не нашел чего-то подобного....
Ну дык, смотрим.  :)
Сейчас этой строки нет.  :)

Гы) т.е. все просто потому, что метод занимающийся работой с сетью - есть часть наследника QThread?
ну ок))) просто я "думаю немного по другому" - для меня сделать так - это сешать 2 разнородные сущности. - управление потоком и работа с сетью.
 У меня в программе был бы отдельный класс в котором собрано все сетевое взаимодействие и который полностью висел-бы внутри потока и собственно такого аспекта просто бы не возникало..

ок. В любом случае спасибо за пояснения в работе QThread.


Название: Re: Многопоточное использование QUdpSocket
Отправлено: BRE от Сентябрь 10, 2010, 18:04
У меня в программе был бы отдельный класс в котором собрано все сетевое взаимодействие и который полностью висел-бы внутри потока и собственно такого аспекта просто бы не возникало..
Точно, поэтому я думаю moveToThread( this ) и не добавили по умолчанию в конструктор QThread.


Название: Re: Многопоточное использование QUdpSocket
Отправлено: Sahab от Сентябрь 10, 2010, 21:44
http://labs.trolltech.com/blogs/2010/06/17/youre-doing-it-wrong/ (http://labs.trolltech.com/blogs/2010/06/17/youre-doing-it-wrong/)
http://blog.exys.org/entries/2010/QThread_affinity.html (http://blog.exys.org/entries/2010/QThread_affinity.html)
...