据我所知,对于大多数在Qt网络中使用线程的情况都是过度和不必要的,特别是如果你以正确的方式使用它并使用readyRead()
信号。但是,我的“客户端”应用程序将同时打开多个套接字(大约5个)。有可能同时有数据进入所有套接字。我真的不会对传入的数据进行任何密集处理。只需将其读入然后发送信号以使用新接收的数据更新GUI。您认为单个线程应用程序应该能够处理所有进入的数据吗?
据我所知,我没有向您展示任何代码,而且我的描述非常含糊,很可能取决于它一旦实施后如何执行,但从一般设计角度和您的专家,您的意见是什么?
答案 0 :(得分:3)
除非您收到真正的高带宽流(例如每秒兆字节而不是每秒千字节),否则单线程设计应该足够了。请记住,操作系统的网络堆栈始终在“后台”运行,接收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()
答案 1 :(得分:0)
在没有线程的情况下实现它,使用线程设计(*),测量数据经历的延迟,确定它是否在可接受的范围内。然后决定是否需要使用线程来更快地捕获它。
根据您的描述,关键瓶颈将是“数据就绪”信号的GUI接收,渲染它。如果你使用发送大量这些信号的方法,你的GUI就会做更多的重新渲染。
如果使用单线程方法,则可以编组网络读取并获取所有更新,然后直接刷新GUI。正如你所描述的那样,这听起来像是争论最少的。
(*尽量避免在你进行线程化时需要整个重写的构造,但是不要花费太多精力使它具有线程证明它实际上需要线程来使其高效,例如不要换行一切都用互斥锁调用)
答案 2 :(得分:0)
我对Qt知之甚少,但这可能是您使用select()
通过单个线程复用多个套接字访问的典型场景。
如果选择的线程主要用于处理来自/到套接字的数据,那么你将非常快(因为你将有更少的上下文切换)。因此,如果您没有传输大量数据,那么单线程解决方案可能会更快。
话虽如此,我会选择最符合您需求的解决方案,这是您可以在相当长的时间内实施的。实现select
(异步)可能非常麻烦,可能不需要过度杀伤。
这是一种类似C的方法,但我希望无论如何我都可以提供帮助。