【发布时间】:2023-03-27 22:22:01
【问题描述】:
如何在不同的线程中执行 QTcpSocket 函数?
【问题讨论】:
-
不可能。我已经学会了这种艰难的方式。见my question。已针对这些套接字和服务器类向 Qt 提出错误建议至
moveToThread () = delete。 QTBUG-82373
标签: c++ multithreading qt tcp qt4
如何在不同的线程中执行 QTcpSocket 函数?
【问题讨论】:
moveToThread () = delete。 QTBUG-82373
标签: c++ multithreading qt tcp qt4
QT 文档明确指出不应跨线程使用QTCPSocket。即,在主线程中创建一个QTCPSocket,并将信号绑定到另一个线程中的对象。
我怀疑您正在实现类似于 Web 服务器的东西,其中侦听在接受时创建 QTCPSocket。然后,您需要另一个线程来处理处理该套接字的任务。你不能。
我的解决方法是将套接字保留在它所在的线程中。我为该线程中的所有传入数据提供服务,并将其放入队列中,另一个线程可以处理该数据。
virtual void incomingConnection(qintptr socketDescriptor)注意:如果在该方法的重新实现中创建了另一个套接字,则需要通过调用
addPendingConnection()将其添加到Pending Connections机制中。注意:如果您想在另一个线程中将传入连接作为新的
QTcpSocket对象处理,您必须将socketDescriptor传递给另一个线程并在那里创建QTcpSocket对象并使用其setSocketDescriptor()方法。
【讨论】:
QTcpSocket 实例(服务器的隐式子节点和您在新线程中创建的实例),这会导致运行时断言失败。
QTcpSocket 移动到另一个工作线程。在检查堆栈跟踪后,似乎主线程仍然保留了一些 QSslSocketBackendPrivate 类型的对象,它写入了数据。这会导致崩溃。过去我误解了这种情况,并将问题归咎于我的编码错误。但似乎 Qt 库有问题。见this。将 TCP 套接字移动到另一个线程的正确方法是什么?
重要的是要注意在线程QTcpSocket 方面可以做什么和不可以做什么:
您可以在非主线程中使用它,但只能在创建它的线程中使用。
您不能从不同线程调用 QTcpSocket 上的不同函数,例如在一个线程中读取,在另一个线程中写入。相反,您可以为每个 QTcpSocket 创建一个单独的线程,以防止它们占用可能在主线程中绘制小部件的时间和资源。
IMO,将您的 IO(包括 QTcpSocket)放在主线程以外的线程中是最佳实践,并且对于任何高性能应用程序都必须这样做。我一直在非主线程中使用QTcpSocket,使用以下成语:
// Read data from a QTcpSocket in a thread. Assumes this is in some class.
m_thread = std::thread([this]
{
QEventLoop eventLoop;
QTcpSocket* socket = new QTcpSocket(&eventLoop);
socket->connectToHost("localhost", 9999);
// enqueue or process the data
QObject::connect(socket, &QTcpSocket::readyRead, &eventLoop, [socket]
{
m_concurrentQueue.push_back(socket->readAll());
});
// Quit the loop (and thread) if the socket it disconnected. You could also try
// reconnecting
QObject::connect(socket, &QTcpSocket::disconnected, &eventLoop, [&eventLoop]
{
eventLoop.quit();
});
eventLoop.exec();
delete socket;
});
其中m_thread 是某个成员线程(基本上只是确保它的生命周期大于当前的直接作用域),而m_concurrentQueue 是某个线程安全队列,或者std 具有互斥保护的容器。
您还需要将一些信号(我通常称之为joinAll)连接到事件循环退出函数,并从类析构函数中调用它。当使用线程中的事件循环习语时,您始终必须小心确保您可以正确地销毁类,否则您的程序将不会退出(或者在 Windows 上它将被终止,通常使用一些析构函数没有被调用,它最终成为一个静默错误)。
我通常也使用条件变量在创建线程后等待,直到事件循环开始。这不是必需的,但如果您将这些线程放在构造函数中,它可以帮助使程序流程更有意义。
【讨论】:
QTcpSocket 或QWebSocket 不是线程可移动的这一事实很难!在这方面向 Qt 提出了一个错误,并将其放在上述问题之下。
m_concurrentQueue 的数据,我的意思是我应该使用QTimer 还是只是通知主线程开始处理队列数据的信号?哪个变体在性能方面最好?谢谢。
&QEventLoop::quit?
将QMutex 锁定所有调用,不仅在“不同”线程上,而且在所有线程上。一种简单的方法是通过QMutexLocker
【讨论】:
QTcpSocket 实例不能共享套接字描述符.此外,QTcpSocket 的单个实例不能被多个线程访问,无论它是否是互斥的。
我在文档中读到的是 QTcpSocket 不应跨线程使用。如果你想在另一个线程中使用它 Qt 文档说你应该在你的线程中创建一个新的 QTcpSocket 实例并在新实例上设置描述符。为此,您需要重新实现 QTcpServer 并使用QTcpServer::incomingConnection。提供了一个简单的例子here。
【讨论】: