考虑一种情况,您需要与设备保持256个tcp连接,仅用于偶尔发送命令。我想并行执行此操作(它需要阻塞直到它得到响应),我正在尝试使用QThreadPool用于此目的,但我有一些疑问,如果可能的话。
我尝试使用QRunnable,但我不确定套接字在线程之间的行为方式(套接字只应在创建它们的线程中使用?)
我也担心这个解决方案的效率,如果有人可以提出一些替代方案,不一定使用QT,我会很高兴。
下面我发布了一些代码片段。
class Task : public QRunnable {
Task(){
//creating TaskSubclass instance and socket in it
}
private:
TaskSubclass *sub;
void run() override {
//some debug info and variable setting...
sub->doSomething( args );
return;
}
};
class TaskSubclass {
Socket *sock; // socket instance
//...
void doSomething( args )
{
//writing to socket here
}
}
class MainProgram : public QObject{
Q_OBJECT
private:
QThreadPool *pool;
Task *tasks;
public:
MainProgram(){
pool = new QThreadPool(this);
//create tasks here
}
void run(){
//decide which task to start
pool->start(tasks[i]);
}
};
答案 0 :(得分:0)
我最喜欢的解决方案是使用select()
多路复用您的套接字。这样你就不需要创建额外的线程了,这是一种“非常POSIX”的方式。
例如,请参阅本教程:
http://www.binarytides.com/multiple-socket-connections-fdset-select-linux/
或相关问题:
答案 1 :(得分:0)
正如OMD_AT已经指出最好的解决方案是使用Select()并让内核为你完成工作: - )
这里有一个Async方法和Syncron多线程方法的例子。
在此示例中,我们创建了10个与google webservice的连接并向服务器发出简单的get请求,我们会测量每种方法中所有连接从Google服务器接收响应所需的时间。
请注意您应该使用更快的网络服务器进行真正的测试,例如本地主机,因为网络延迟会对结果产生很大影响。
#include <QCoreApplication>
#include <QTcpSocket>
#include <QtConcurrent/QtConcurrentRun>
#include <QElapsedTimer>
#include <QAtomicInt>
class Task : public QRunnable
{
public:
Task() : QRunnable() {}
static QAtomicInt counter;
static QElapsedTimer timer;
virtual void run() override
{
QTcpSocket* socket = new QTcpSocket();
socket->connectToHost("www.google.com", 80);
socket->write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n");
socket->waitForReadyRead();
if(!--counter) {
qDebug("Multiple Threads elapsed: %lld nanoseconds", timer.nsecsElapsed());
}
}
};
QAtomicInt Task::counter;
QElapsedTimer Task::timer;
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
// init
int connections = 10;
Task::counter = connections;
QElapsedTimer timer;
/// Async via One Thread (Select)
// handle the data
auto dataHandler = [&timer,&connections](QByteArray data) {
Q_UNUSED(data);
if(!--connections) qDebug(" Single Threads elapsed: %lld nanoseconds", timer.nsecsElapsed());
};
// create 10 connection to google.com and send an http get request
timer.start();
for(int i = 0; i < connections; i++) {
QTcpSocket* socket = new QTcpSocket();
socket->connectToHost("www.google.com", 80);
socket->write("GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n");
QObject::connect(socket, &QTcpSocket::readyRead, [dataHandler,socket]() {
dataHandler(socket->readAll());
});
}
/// Async via Multiple Threads
Task::timer.start();
for(int i = 0; i < connections; i++) {
QThreadPool::globalInstance()->start(new Task());
}
return app.exec();
}
打印:
Multiple Threads elapsed: 62324598 nanoseconds
Single Threads elapsed: 63613967 nanoseconds
答案 2 :(得分:0)
虽然答案已被接受,但我想与大家分享一下
我从您的问题中理解的内容: 有256个当前有效的连接,有时您会发送请求(&#34;命令&#34;当您命名时它)其中一个并等待响应。同时,你想让这个过程多线程,虽然你说&#34;它需要阻塞直到它得到响应&#34;,我假设你暗示阻塞了一个处理请求 - 响应的线程过程,但不是主线程。
如果我确实理解了这个问题,我建议使用Qt:
#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);
}
现在,您可以使用从线程池中获取的单独线程向套接字发送命令:引擎盖QtConcurrent::run()
使用Qt为您提供的QThreadPool
的全局实例。该线程阻塞,直到它得到响应,然后用responseHandler
处理它。同时,管理所有命令和套接字的主线程保持畅通无阻。只需捕获done()
信号,该信号表示已收到并成功处理了响应。
有一点需要注意:异步版只有在线程池中有空闲线程时才会发送请求,否则会等待它。当然,这是任何线程池的行为(这正是这种模式的重点),但不要忘记这一点。
此外,我正在编写没有Qt的代码,因此可能包含一些错误。
编辑:事实证明,这不是线程安全的,因为套接字在Qt中不可重入。
您可以做的是将互斥锁与套接字关联,并在每次执行其功能时将其锁定。这可以轻松地创建QTcpSocket类的包装器。如果我错了,请纠正我。