1

我知道在大多数情况下,在 Qt 网络中使用线程是多余的和不必要的,特别是如果你以正确的方式使用并使用readyRead()信号。但是,我的“客户端”应用程序将同时打开多个套接字(大约 5 个)。有可能同时有数据进入所有套接字。我真的不会对传入的数据进行任何密集处理。只需将其读入,然后发出信号以使用新接收的数据更新 GUI。你认为单线程应用程序应该能够处理所有传入的数据吗?

我知道我没有向您展示任何代码,并且我的描述非常模糊,并且很可能取决于它在实施后的表现,但是从一般设计的角度和你们的专业知识来看,您的意见是什么?

4

3 回答 3

4

除非您正在接收真正的高带宽流(例如每秒兆字节而不是每秒千字节),否则单线程设计就足够了。请记住,操作系统的网络堆栈始终在“后台”运行,接收 TCP 数据包并将接收到的数据存储在固定大小的内核内存缓冲区中。这与您的程序执行并行发生,因此在大多数情况下,您的程序是单线程并忙于处理 GUI 更新(或另一个套接字)这一事实不会妨碍您的计算机接收 TCP 数据包。

单线程设计导致 TCP 流量变慢的情况是,如果您的程序(通过 Qt)没有足够快地调用 recv(),从而导致内核的套接字 TCP 接收缓冲区完全充满了数据。那时内核别无选择,只能开始丢弃该套接字的传入 TCP 数据包,这将导致服务器必须重新发送这些 TCP 数据包,这将导致套接字的 TCP 接收速度减慢,至少暂时地。但是,可以通过确保缓冲区永远不会(或至少很少)变满来避免这个问题。

显而易见的方法是确保您的程序尽可能快地读取所有传入的数据——这是 QTCPSocket 默认执行的操作。您唯一需要做的就是确保您的 GUI 更新不会花费过多的时间——并且 Qt 的小部件更新例程相当有效,所以它们不应该,除非您有一个非常精细的 GUI 或低效的自定义paintEvent() 例程等。

如果这还不够,您可以做的下一件事(如有必要)是告诉操作系统的 TCP 堆栈增加其内核 TCP 接收缓冲区的大小,例如通过执行以下操作:

 int fd = myQTCPSocketObject.descriptor();
 int newBufSizeBytes = 128*1024;   // request 128kB kernel recv-buffer for this socket
 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &newBufSizeBytes, sizeof(newBufSizeBytes)) != 0) perror("setsockopt");

这样做会让你的(单个)线程有更多的时间在传入的数据包开始因为缺乏内核缓冲区空间而被丢弃之前做出反应。

如果在尝试了所有这些之后,您仍然没有获得所需的网络性能,那么您可以尝试使用多线程。我怀疑它会出现这种情况,但如果确实如此,它不会对你的程序设计产生太大影响;您只需编写一个包装类(称为 SocketThread 或其他东西),它包含您的 QTCPSocket 对象并运行一个处理从套接字读取的内部线程,并在线程从套接字读取数据时发出 bytesReceived(QByteArray) 信号。您的其余代码将保持大致相同;只需修改它以保存 SocketThread 对象而不是 QTCPSocket,并将 SocketThread 的 bytesReceived(QByteArray) 信号连接到相应的插槽(当然,为了线程安全,通过 QueuedConnection)并使用它而不是直接响应 readReady() .

于 2013-11-14T08:33:38.147 回答
1

我对 Qt 了解不多,但这可能是一个典型的场景,您可以使用select()单个线程来多路复用多个套接字访问。

如果用于选择的线程主要用于处理来自/到套接字的数据,那么您将非常快(因为您将有更少的上下文切换)。因此,如果您没有传输真正大量的数据,那么单线程解决方案可能会更快。

话虽如此,我会选择最适合您需求的解决方案,您可以在相当长的时间内实施。实现select(异步)可能非常麻烦,可能不需要的过度杀伤力。

这是一种类似 C 的方法,但我希望无论如何我都能提供帮助。

于 2013-11-14T09:04:33.647 回答
1

在没有线程的情况下实现它,使用考虑线程的设计(*),测量您的数据体验的延迟,确定它是否在可接受的范围内。然后决定是否需要使用线程来更快地捕获它。

根据您的描述,关键瓶颈将是“数据就绪”信号的 GUI 接收,渲染它。如果您使用发送大量这些信号的方法,您的 GUI 将会进行更多的重新渲染。

如果您使用单线程方法,您可以编组网络读取并获取所有更新,然后直接刷新 GUI。正如您所描述的,这听起来像它的竞争程度最低。

(* 尽量避免在线程化时需要整个重写的结构,但不要花太多精力使其成为线程证明的,以至于它实际上需要线程来使其高效,例如不要用互斥锁包装所有东西来电)

于 2013-11-14T02:38:34.853 回答