Obwohl, ist die Antwort bereits akzeptiert, ich möchte meine)
Was ich aus Ihrer Frage verstanden teilen: 256 Mit derzeit aktiv Von Zeit zu Zeit senden Sie eine Anfrage ("Befehl", wie Sie es genannt haben) an eine von ihnen und warten auf die Antwort. In der Zwischenzeit möchten Sie diesen Prozess Multithread machen, und obwohl Sie sagten "Es muss blockieren, bis es die Antwort erhält", nehme ich an, Sie implizierte blockieren einen Thread, der Anfrage-Antwort Prozess behandelt, aber nicht den Hauptthread.
Wenn ich in der Tat die Frage richtig verstehen, ist hier, wie ich vorschlagen, es mit Qt zu tun:
#include <functional>
#include <QObject> // need to add "QT += core" in .pro
#include <QTcpSocket> // QT += network
#include <QtConcurrent> // QT += concurrent
#include <QFuture>
#include <QFutureWatcher>
class CommandSender : public QObject
{
public:
// Sends a command via connection and blocks
// until the response arrives or timeout occurs
// then passes the response to a handler
// when the handler is done - unblocks
void SendCommand(
QTcpSocket* connection,
const Command& command,
void(*responseHandler)(Response&&))
{
const int timeout = 1000; // milliseconds, set it to -1 if you want no timeouts
// Sending a command (blocking)
connection.write(command.ToByteArray()); // Look QByteArray for more details
if (connection.waitForBytesWritten(timeout) {
qDebug() << connection.errorString() << endl;
emit error(connection);
return;
}
// Waiting for a response (blocking)
QDataStream in{ connection, QIODevice::ReadOnly };
QString message;
do {
if (!connection.waitForReadyRead(timeout)) {
qDebug() << connection.errorString() << endl;
emit error(connection);
return;
}
in.startTransaction();
in >> message;
} while (!in.commitTransaction());
responseHandler(Response{ message }); // Translate message to a response and handle it
}
// Non-blocking version of SendCommand
void SendCommandAsync(
QTcpSocket* connection,
const Command& command,
void(*responseHandler) (Response&&))
{
QFutureWatcher<void>* watcher = new QFutureWatcher<void>{ this };
connect(watcher, &QFutureWatcher<void>::finished, [connection, watcher]()
{
emit done(connection);
watcher->deleteLater();
});
// Does not block,
// emits "done" when finished
QFuture<void> future
= QtConcurrent::run(this, &CommandSender::SendCommand, connection, command, responseHandler);
watcher->setFuture(future);
}
signals:
void done(QTcpSocket* connection);
void error(QTcpSocket* connection);
}
Jetzt können Sie einen Befehl an eine Steckdose mit einem separaten Thread von einem Thread-Pool genommen senden: unter Die Haube QtConcurrent::run()
verwendet die von Qt für Sie bereitgestellte globale Instanz QThreadPool
. Dieser Thread blockiert, bis er eine Antwort zurückbekommt, und behandelt sie dann mit responseHandler
. Währenddessen bleibt Ihr Hauptthread, der alle Ihre Befehle und Sockets verwaltet, nicht blockiert. Fangen Sie einfach done()
Signal, das sagt, dass die Antwort empfangen und erfolgreich behandelt wurde.
Eine Sache zu beachten: asynchrone Version sendet Anfrage nur, wenn es einen freien Thread im Thread-Pool gibt und darauf wartet, andernfalls. Natürlich ist dies das Verhalten für jeden Thread-Pool (das ist genau der Punkt eines solchen Musters), aber vergessen Sie das nicht.
Auch ich schrieb Code ohne Qt in handliche, so kann einige Fehler enthalten.
Edit: Wie sich herausstellte, ist dies nicht Thread-sicher, da Sockets nicht in Qt reentrant sind.
Sie können einen Mutex mit einem Socket verknüpfen und ihn jedes Mal sperren, wenn Sie seine Funktion ausführen. Dies kann leicht geschehen, indem ein Wrapper für die QTcpSocket-Klasse erstellt wird. Bitte korrigieren Sie mich, wenn ich falsch liege.
kann ich fragen, warum Sie mehrere Threads für die Lösung verwenden möchten? Das Event-System von Qt ist in der Lage, hunderte von Tcp-Verbindungen über einen Thread ohne Probleme zu bewältigen, oder blockiert die "socket data handler" -Logik den Haupt-Thread? – Spiek
Danke für den Kommentar, ich habe gerade Post bearbeitet. Es muss blockieren, bis es die Antwort erhält. – Vido
Gibt es einen Grund, warum Sie nicht Signal und Slot-Mechanismus verwenden? Sie könnten einfach das ReadyRead-Signal der QTcpSocket verwenden und es mit einem Steckplatz verbinden. – Spiek