Qt синхронизация потоков
Модератор: Модераторы разделов
Qt синхронизация потоков
Есть два класса Class1, потомок QObject, и Class2, потомок QThread. В Class1 есть слот some_slot(char *buf). В Class2 есть сигнал some_signal(char *buf).
В Class1 есть два экземпляра Class2. Class2 принимает Udp пакеты (оба экземпляра на разных интерфейсах) и затем с помощью "emit some_signal" передаёт пришедший пакет основному потоку, который их отображает. У меня три вопроса
1) Если пакеты приходят на 2 интерфейса одновременно, то в buf в одном из случаев получается каша. Как это исправить?
2) Вообще можно так делать? с помощью emit передавать данные в основной поток?
3) Если нет, то как сделать чтобы при приходе пакета, данные передать основному потоку?
В Class1 есть два экземпляра Class2. Class2 принимает Udp пакеты (оба экземпляра на разных интерфейсах) и затем с помощью "emit some_signal" передаёт пришедший пакет основному потоку, который их отображает. У меня три вопроса
1) Если пакеты приходят на 2 интерфейса одновременно, то в buf в одном из случаев получается каша. Как это исправить?
2) Вообще можно так делать? с помощью emit передавать данные в основной поток?
3) Если нет, то как сделать чтобы при приходе пакета, данные передать основному потоку?
Re: Qt синхронизация потоков
Синхронизация потоков через сиглалы-слоты -- далеко не лучший вариант, т.к. это фактически прямой вызов функции.
создай разделяемую память, и пусть одни потоки в неё пишет, другой из нее читает
это позволит избежать блокировок
для каждой пары классов -- своя разделяемая память. Основной класс можешь отправлять поспать, а по выходу из сна он будет проверять появились ли данные. Если ничего нет -- далее спать.
создай разделяемую память, и пусть одни потоки в неё пишет, другой из нее читает
это позволит избежать блокировок
для каждой пары классов -- своя разделяемая память. Основной класс можешь отправлять поспать, а по выходу из сна он будет проверять появились ли данные. Если ничего нет -- далее спать.
phenom x4 905e, asus m4a79 deluxe, 4 gb, ati x550, ati 4350, 2 x 17" LCD
Re: Qt синхронизация потоков
А если основной класс - главное окно приложения, и если его "отправить поспать", окно будет реагировать на пользовательские сообщения, например на нажатие клавиши или кнопки мыши?
Re: Qt синхронизация потоков
а это смотря как Вы его отправите спать, если просто скажите ждать таймер, то он попутно и другими делами сможет заниматься. Но если у Вас промежутки будут маленькими, то таймер желательно останавливать
ЗЫ вообще всё есть в документации по Qt, так же там есть примеры использования многопоточности
ЗЫ вообще всё есть в документации по Qt, так же там есть примеры использования многопоточности
phenom x4 905e, asus m4a79 deluxe, 4 gb, ati x550, ati 4350, 2 x 17" LCD
Re: Qt синхронизация потоков
Я бы не был так категоричен -- посмотри что из себя представляет макрос Q_OBJECT и исходники классов QObject и QMetaObject (файл qobject.cpp)
я писал эмуляцию поведения Q_OBJECT и видел как это работает в отладчике, от emit signal до вызова slot не так много вызовов.
кстати, там относительно простая структура, благодаря чему возможно для разных экземпляров одного класса иметь разный набор сигналов-слотов.
я писал эмуляцию поведения Q_OBJECT и видел как это работает в отладчике, от emit signal до вызова slot не так много вызовов.
кстати, там относительно простая структура, благодаря чему возможно для разных экземпляров одного класса иметь разный набор сигналов-слотов.
phenom x4 905e, asus m4a79 deluxe, 4 gb, ati x550, ati 4350, 2 x 17" LCD
Re: Qt синхронизация потоков
vadiml писал(а): ↑04.02.2008 15:39Я бы не был так категоричен -- посмотри что из себя представляет макрос Q_OBJECT и исходники классов QObject и QMetaObject (файл qobject.cpp)
я писал эмуляцию поведения Q_OBJECT и видел как это работает в отладчике, от emit signal до вызова slot не так много вызовов.
И смотрел, и трейсил, и хакал. См. здесь: http://doc.trolltech.com/4.3/threads.html#...-across-threads
Re: Qt синхронизация потоков
Ну подскажите, как можно из одного потока в другой передать большой массив, длиной 0xffff? А то, как я уже говорил, если приходят пакеты почти одновременно, или каша в одном из пакетов, или оба пакета одинаковых
Кстати в ссылке выше говорится про Qt 4.3, у меня 4.2, это имеет значение?
Кстати в ссылке выше говорится про Qt 4.3, у меня 4.2, это имеет значение?
Re: Qt синхронизация потоков
Покажите код. Так мало что понятно.
Кстати в ссылке выше говорится про Qt 4.3, у меня 4.2, это имеет значение?
Вряд ли.
Re: Qt синхронизация потоков
class1.h
class1.cpp
class2.h
class2.cpp
Я здесь тока Class1 и Class2 поменял
Код: Выделить всё
#ifndef CLASS1_H
#define CLASS1_H
#include <QThread>
#include <QReadWriteLock>
class Class1 : public QThread
{
Q_OBJECT
public:
Class1(QString group, QString iface, int id);
void stop();
void setMulticast();
u_char buf[0xffff];
signals:
void recvPack(int id, int size);
protected:
void run();
private:
void initNet();
bool stopped;
int ident;
QReadWriteLock lock;
QString group;
QString iface;
int port;
int sock;
};
#endif
class1.cpp
Код: Выделить всё
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "class1.h"
Class1::Class1(QString g, QString i, int id)
{
stopped = false;
group = g;
iface = i;
ident = id;
port = 50138;
initNet();
start();
}
void Class1::run()
{
while (1)
{
lock.lockForWrite();
if (stopped)
{
stopped = false;
break;
}
lock.unlock();
sockaddr_in cli;
int recvbyte;
socklen_t addrsize;
addrsize = sizeof(cli);
recvbyte = recvfrom(sock, buf, 0xffff, 0, (struct sockaddr *)&cli, &addrsize);
emit recvPack(ident, recvbyte);
}
}
void Class1::stop()
{
lock.lockForWrite();
stopped = true;
close(sock);
lock.unlock();
}
void Class1::setMulticast()
{
struct ip_mreq mreq;
bzero(&mreq, sizeof(mreq));
inet_aton((const char *)(group.toLocal8Bit()), &mreq.imr_multiaddr);
inet_aton((const char *)(iface.toLocal8Bit()), &mreq.imr_interface);
setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
}
void Class1::initNet()
{
struct sockaddr_in srv;
bzero(&srv, sizeof(srv));
srv.sin_family = AF_INET;
srv.sin_port = htons(port);
inet_aton((const char *)(group.toLocal8Bit()), &srv.sin_addr);
sock = socket(AF_INET, SOCK_DGRAM, 0);
bind(sock, (struct sockaddr *)&srv, sizeof(srv));
}
class2.h
Код: Выделить всё
#ifndef CLASS2_H
#define CLASS2_H
#include <QObject>
#include <QString>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "class1.h"
class Class2 : public QObject
{
Q_OBJECT
public:
Class2(QString group, QString iface, QObject *parent = 0);
u_char data[0xffff];
private slots:
void recvPack(int id, int size);
private:
Class1 *multicastThread;
Class1 *ifaceThread;
};
#endif
class2.cpp
Код: Выделить всё
#include "class2.h"
Class2::Class2(QString group, QString iface, QObject *parent) : QObject(parent)
{
multicastThread = new Class1(group, iface, 1);
multicastThread->setMulticast();
ifaceThread = new Class1(iface, "", 2);
connect(multicastThread, SIGNAL(recvPack(int, int)), this, SLOT(recvPack(int, int)));
connect(ifaceThread, SIGNAL(recvPack(int, int)), this, SLOT(recvPack(int, int)));
}
void Class2::recvPack(int id, int size)
{
if (id == 1)
{
memcpy(data, multicastThread->buf, size);
}
else if (id == 2)
{
memcpy(data, ifaceThread->buf, size);
}
for (int i=0; i<size; i++)
{
if ((i % 16) == 0)
{
fprintf(stderr,"\n");
}
if (data[i] < 16)
{
fprintf(stderr,"0x0%x, ",data[i]);
}
else
{
fprintf(stderr,"0x%x, ",data[i]);
}
}
fprintf(stderr,"\n");
}
Я здесь тока Class1 и Class2 поменял
Re: Qt синхронизация потоков
Ну помогите! Третий день бьюсь, не знаю что делать...
Re: Qt синхронизация потоков
В коде невооружённым глазом видны кучи гонок.
Попробую перечислить.
Поток запускается сразу же в конструкторе. Там же инициализируется сокет. Представьте такую ситуацию:
Аналогично со вторым потоком.
Метод stop просто некорректен.
Но самое интересное начинается здесь.
Разберёмся, что здесь происходит. Мы считываем 65535 байтов и посылаем сигнал другому потоку. Сигнал ложится в очередь сообщений другого потока. Мы пока ещё работаем. Идём на следующий заход, опять считываем данные и опять посылаем сигнал.
Теперь ОС передаёт управление главному потоку. Главный поток проверяет свою очередь сообщений, видит наш первый сигнал, читает его и вызывает соответствующую функцию. Вот только в этой функции уже буфер buf перезаписан данными следующего пакета. Упс.
Я вижу два варианта: либо лочить ресурс, пока его не считали (тогда будет тратиться меньше памяти, но будут большие проблемы с масштабируемостью), либо копировать весь буфер и передавать его в качестве аргумента (больше памяти, но лучше масштабируемость).
А вообще написано очень небрежно. Параллельное программирование не любит такого стиля, у вас будет много подобных проблем. Старайтесь писать очень аккуратно.
Попробую перечислить.
Поток запускается сразу же в конструкторе. Там же инициализируется сокет. Представьте такую ситуацию:
multicastThread = new Class1(group, iface, 1);
<...>
Здесь ОС приостанавливает главный поток и передаёт управление потоку Class1. Он тут же инициализирует сеть, принимает данные и посылает сигнал recvPack. Но сигнал ещё ни с чем не соединён, упс. Данные пропали.
<...>
connect(ifaceThread, SIGNAL(recvPack(int, int)), this, SLOT(recvPack(int, int)));
Аналогично со вторым потоком.
Метод stop просто некорректен.
Но самое интересное начинается здесь.
recvbyte = recvfrom(sock, buf, 0xffff, 0, (struct sockaddr *)&cli, &addrsize);
emit recvPack(ident, recvbyte);
Разберёмся, что здесь происходит. Мы считываем 65535 байтов и посылаем сигнал другому потоку. Сигнал ложится в очередь сообщений другого потока. Мы пока ещё работаем. Идём на следующий заход, опять считываем данные и опять посылаем сигнал.
Теперь ОС передаёт управление главному потоку. Главный поток проверяет свою очередь сообщений, видит наш первый сигнал, читает его и вызывает соответствующую функцию. Вот только в этой функции уже буфер buf перезаписан данными следующего пакета. Упс.
Я вижу два варианта: либо лочить ресурс, пока его не считали (тогда будет тратиться меньше памяти, но будут большие проблемы с масштабируемостью), либо копировать весь буфер и передавать его в качестве аргумента (больше памяти, но лучше масштабируемость).
А вообще написано очень небрежно. Параллельное программирование не любит такого стиля, у вас будет много подобных проблем. Старайтесь писать очень аккуратно.
Re: Qt синхронизация потоков
"Небрежно" - можно поконкретнее? Программированием не так давно занимаюсь, хотелось бы знать, что не так
И как можно скопировать весь буфер и передать его в качестве парметра? Выделить память, скопировать в неё весь массив и передать указатель в качестве параметра?
В основном потоке можно будет потом эту память освободить или надо в том же потоке?
И как можно скопировать весь буфер и передать его в качестве парметра? Выделить память, скопировать в неё весь массив и передать указатель в качестве параметра?
В основном потоке можно будет потом эту память освободить или надо в том же потоке?
Re: Qt синхронизация потоков
ОК. Перечислю всё, что мне не понравилось.
1. Параметры типа QString передаются по значению. Это плохо. Практически всегда все параметры непримитивного типа (примитивные типы - int, float, ...) нужно передавать по константной ссылке.
2. Имеется public-поле. Это нарушает один из основных принципов ООП - инкапсуляцию.
3. В Qt есть свои средства для работы с сокетами, не уверен, что они вам подойдут, но можно попробовать.
4. Нет вообще никакой обработки ошибок.
5. Не совсем удачный дизайн классов. Я бы сделал два подкласса класса Class1. Между ними хоть различие хоть и небольшое, но есть. По моему опыту в дальнейшем оно будет только увеличиваться.
6. Есть такая штука - список инициализации конструктора, лучше её применять, чем в теле присваивать.
И как можно скопировать весь буфер и передать его в качестве парметра? Выделить память, скопировать в неё весь массив и передать указатель в качестве параметра?
Да.
В основном потоке можно будет потом эту память освободить или надо в том же потоке?
Где угодно можно, glibc потокобезопасна.
А ещё лучше использовать класс QVector, он сам всё, что надо, скопирует и освободит.
Re: Qt синхронизация потоков
Спасибо.
Я смотрел, не мог найти как сделать чтобы сокет пакеты принимал предназначенные адресу 234.0.0.1
Обработка ошибок есть, я просто не стал писать её.
Спасибо ещё раз
3. В Qt есть свои средства для работы с сокетами, не уверен, что они вам подойдут, но можно попробовать.
Я смотрел, не мог найти как сделать чтобы сокет пакеты принимал предназначенные адресу 234.0.0.1
Обработка ошибок есть, я просто не стал писать её.
Спасибо ещё раз
Re: Qt синхронизация потоков
Спасибо.
Я смотрел, не мог найти как сделать чтобы сокет пакеты принимал предназначенные адресу 234.0.0.1
Обработка ошибок есть, я просто не стал писать её.
Спасибо ещё раз
3. В Qt есть свои средства для работы с сокетами, не уверен, что они вам подойдут, но можно попробовать.
Я смотрел, не мог найти как сделать чтобы сокет пакеты принимал предназначенные адресу 234.0.0.1
Обработка ошибок есть, я просто не стал писать её.
Спасибо ещё раз
Re: Qt синхронизация потоков
Пытаюсь подключить <QUdpSocket>, говорит No such file or directory
Тоже самое на <QtNetwork>
В папке /usr/include/qt4 все эти файлы есть.
Тоже самое на <QtNetwork>
В папке /usr/include/qt4 все эти файлы есть.