Я довольно новичок в QThread, и я хочу сделать многопоточное приложение с Qt. Есть много онлайн-источников, некоторые рекомендуют подклассифицировать QThread, некоторые говорят, что это неверно, и они используют функцию moveToThread. Я действительно смущен. Возможно, это зависит от требований, поэтому я пишу свои требования.Нужна помощь для многопоточного приложения с использованием QThread
Программное обеспечение будет связываться с устройством по протоколу UDP. Устройство отправляет полученный пакет обратно отправителю в качестве ACK. Поскольку UDP не является надежным, я хочу добавить некоторую надежность в связь, проверив ответ с устройства. Я хочу дать немного времени устройству для отправки ответа. Если ответ (пакет ACK) не приходит, я хочу отправить тот же пакет снова. Если приходит неправильный пакет ответов (из пакетов с последовательностью), я могу игнорировать его, не нужно повторно их повторять. Будут некоторые пакеты (команды для устройства), которые будут периодически отправляться, например, каждую секунду.
Я хочу сделать все общение в потоке с помощью QThread. Поток запускается нажатием кнопки, и связь начнется (некоторые пакеты будут отправляться периодически). Связь остановится, нажав кнопку.
Так как я могу это сделать? Мне нужны только шаги.
EDIT:
Я уже impelemented блокирование чтения и записи. Я перемещаю его в Thread с помощью функции moveToThread и запускает поток. Но я не знаю, лучший ли это для моего случая использования.
я отправить несколько дейтаграмм, один за другим, как следующее, например:
int UDP::readCalibration(..)
{
while(readStatus() != 0);
// Send addr, read page command and read buffer command
// one after each
writeRegisterBlock(LOW_ADDR, CalibAddr, 2); // Blocking
writeRegister(CMD_Reg, READ_PAGE); // Blocking
readRegisterBlock(READ_BUF, data, 128); // Blocking
}
....
int UDP::writeRegisterBlock(...)
{
....// Build UdpPacket
return UDP_Send(UdpPacket, &ReceivePacket);
}
int UDP::writeRegister(...)
{
....// Build UdpPacket
return UDP_Send(UdpPacket, &ReceivePacket);
}
int UDP::readRegisterBlock(...)
{
....// Build UdpPacket
return UDP_Send(UdpPacket, &ReceivePacket);
}
// and UDP_Send function, this is the important part
int UDP::UDP_Send(QByteArray UdpPacket, QByteArray *ReceivePacket)
{
QByteArray Datagram;
QHostAddress SenderAddress;
quint16 SenderPort;
int UDP_RetVal = -1, timeOutCounter = -1, NrOfSend = -1, NrOfRecv = -1, retVal = -1;
bool pendingDatagram = false;
SetContinueToRecv(true); // sets a boolean variable with mutex
SetContinueToSend(true); // sets a boolean variable with mutex
while ((NrOfSend < MAX_RETRY) && GetContinueToSend())
{
NrOfSend++;
SetContinueToRecv(true); // start receiver loop
UDP_RetVal = udpSocket->writeDatagram(UdpPacket, DestinationIP, port);
if(UDP_RetVal < 0)
{
qDebug() << "udpSocket error";
}
else
{
while((NrOfRecv < MAX_RETRY) && GetContinueToRecv())
{
pendingDatagram = false;
while (!pendingDatagram && (timeOutCounter < TIMEOUT_MS))
{
msleep(SLEEP_MS);
timeOutCounter++;
pendingDatagram = udpSocket->hasPendingDatagrams();
}
if (timeOutCounter < TIMEOUT_MS)
{
Datagram.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(Datagram.data(), Datagram.size(), &SenderAddress, &SenderPort);
//Compare only first 4 bytes, cmd, nr, addr
if((UdpPacket.mid(0, 4) == Datagram.mid(0, 4)) && (SenderAddress == DestinationIP))
{
retVal = 0;
//Take only data , not commands
ReceivePacket->resize(Datagram.size()-4);
ReceivePacket->replace(0, Datagram.size()-4, Datagram.right(Datagram.size()-4));
SetContinueToRecv(false); // Break receiver while loop
SetContinueToSend(false); // Break sender while loop
}
else // Wrong packet
{
SetContinueToRecv(true); // try again to receive
timeOutCounter = -1;
qDebug() << "Wrong Packet.";
} // Wrong packet
}
else // Timeout
{
qDebug() << "Timeout maximum. NrOfSend: " << NrOfSend << " NrOfRecv: " << NrOfRecv;
timeOutCounter = -1;
SetContinueToSend(true); // try again to send
SetContinueToRecv(false);
} // Timeout
} // Receive loop
NrOfRecv = -1;
} // Succesfully sent
} // Send loop
if(NrOfSend == MAX_RETRY)
{
retVal = -1; // Could not send.
qDebug() << "Could not send";
}
return retVal;
}
Как я могу заменить функции блокировки с функциями readyRead и тайм-аута? Возможно, мне нужно сохранить отправленную дейтаграмму и в таймаут мне нужно сравнить полученную дейтаграмму с отправленной дейтаграммой, если она такая же, пакет отправляется и принимается правильно. Если нет, я должен отправить его снова?
Подклассификация 'QObject' и использование' moveToThread' - это, как правило, путь. Я думаю, что это хорошо объяснено в [документации] (http://doc.qt.io/qt-5/qthread.html#details). Вы еще это выяснили? – thuga
Я кратко прочитал аргументы о том, как использовать QThread, и, как и вы, я пришел к выводу, что все это сбивает с толку, и я перестала читать. Суб-класс QThread и повторная реализация функции run() имеют для меня наибольший смысл. Относитесь к реализации run() как main() для нового потока. Создайте в нем объекты (в стеке), как и в main(). Следует помнить, что объекты, созданные в main(), относятся к основному потоку, а объекты, созданные в run(), относятся к этому потоку. Мне никогда не приходилось использовать функцию moveToThread(). thuga, и я вижу, что это по-другому выглядит :-( –
Если вы хотите иметь класс реентера, который работает в другом потоке, то использование 'moveToThread' намного проще, чем подклассификация' QThread' для меня. Это позволяет использовать слоты вам не придется беспокоиться о блокировке мьютекса, а что нет при отправке сообщений по потокам. – thuga