带轮询的非阻塞套接字
几天前,我必须调查一个问题,即我的应用程序在(显然)处于空闲状态时显示出异常高的 CPU 使用率。我将问题追溯到一个循环,该循环旨在阻止 recvfrom
调用,而套接字已设置为 O_NONBLOCK
- 导致自旋锁。有两种方法可以解决该问题:将套接字设置为阻塞或使用 poll
或 select
轮询套接字上的可用数据。我选择前者,因为它更简单。但我想知道为什么有人会创建一个非阻塞套接字,然后单独轮询它。阻塞套接字不也有同样的作用吗?使用非阻塞套接字和轮询组合的用例有哪些?在一般情况下它有什么优点吗?
A couple of days ago I had to investigate a problem where my application was showing abnormally high CPU usage when it was (apparently) in idle state. I tracked the problem down to a loop which was meant to block on a recvfrom
call while the socket had been set to O_NONBLOCK
-ing resulting in a spin lock. There were two ways of solving the problem: set the socket to blocking or poll for available data on the socket using poll
or select
. I chose the former as it was simpler. But I am wondering why any one would create a non-blocking socket and then poll on it separately. Doesn't a blocking socket do the same? What are the uses cases where one would use a non-blocking socket and poll combination? Are there any advantages to it in general cases?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
将
poll()
或select()
与非阻塞文件描述符一起使用有两个优点:如果您只有一个文件描述符(套接字)需要等待,并且您不介意无限期地等待它,那么可以;你可以只使用阻塞调用。
第二个优点确实是
select()
及其朋友的杀手级用例。这意味着您可以处理多个套接字连接,以及标准输入和标准输出以及可能的文件 I/O,所有这些都可以通过单个控制线程来处理。Using
poll()
orselect()
with a non-blocking file descriptor gives you two advantages:If you only have a single file descriptor (socket) to wait for, and you don't mind waiting indefinitely on it, then yes; you can just use a blocking call.
The second advantage is really the killer use case for
select()
and friends. It means that you can handle multiple socket connections, as well as standard input and standard output and possibly file I/O, all with a single thread of control.我在这里发帖,因为虽然这个问题很旧。它以某种方式出现在我的谷歌搜索中,并且肯定没有得到正确的回答。
接受的答案只是强调了使用非阻塞套接字的两个优点,但并没有真正深入细节或回答实际问题。
至于何时使用其中一种与另一种相比……一般来说,阻塞套接字仅在在线代码片段中使用。在所有(好的)生产应用程序中都使用非阻塞套接字。我不是无知,如果您知道使用阻塞套接字的实现(并且确信这很可能与线程结合使用) - 或者让我们更具体地在单个线程中使用阻塞套接字 - 请让我知道。
现在我可以给你一个非常容易理解的例子,还有很多其他的例子。让我们以游戏服务器为例。无论玩家是否提供输入(鼠标/键盘)来更改游戏状态,游戏都会按一定的时间间隔进行,游戏状态也会按一定的间隔进行。现在,当套接字在多人游戏中发挥作用时 - 如果您要使用阻塞套接字,除非玩家发送更新,否则游戏状态不会前进 - 因此,如果他们遇到互联网问题,游戏状态将永远不会一致更新并将更改传播给所有玩家。你将会有一个相当不稳定的经历。
现在使用非阻塞套接字,您可以在单线程上运行游戏服务器,更新游戏状态以及套接字,并具有......比方说 50 毫秒的超时间隔 - 并且套接字数据仅在以下情况下从连接的用户读取他们实际上发送了一些东西,然后输入到服务器模拟中,进行处理并输入到下一个tick的游戏状态计算中。
I´m posting here, because although the question is old. It came up in my google search somehow and has definitely not been answered properly.
The accepted answer merely highlights two advantages of using non-blocking sockets but does not really go into detail or answer the actual question.
As to when you would you use one compared to the other ... in general blocking sockets are only used in online code snippets. In all (good) production applications non-blocking sockets are used. I´m not ignorant, if you know of an implementation that uses blocking sockets (and sure that´s very well possible in combination with threads) - or let´s be more specific that uses blocking sockets in a single thread - please do let me know.
Now I can give you a very easy to understand example, and there are many others out there. Let´s take the example of a gaming server. Games advances at ticks, regular intervals where the game state progresses whether or not the player provides input (mouse / keyboard) to change the state of the game. Now when sockets come into play in Multiplayer games - if you were to use blocking sockets the game state would not advance unless the players were sending updates - so if they have internet problems, the game state would never consistently update and propagate changes to all players. You would have a rather choppy experience.
Now using non-blocking sockets, you can run the gameserver on a single-thread, updating the gamestate as well as the sockets, with a ... let´s say 50ms timeout interval - and socket data is only read from connected users when they actually send something, and then fed into the server simulation, processed and fed into the game state calculation for the next tick.
这种情况通常称为“紧循环”。
您确定其他代码部分尚未使用
poll()
(或select()
)并期望套接字处于非阻塞模式吗?否则,那么是的,切换到阻塞模式是最简单的解决方案。
最佳向后兼容的解决方案是在调用
recvfrom()
之前使用poll()
等待套接字变得可读。这样可以确保代码的其他部分能够像以前一样精确地工作。对于
recvfrom()
的情况,我不知道有什么重大区别。可能是一个简单的编码错误。或者有人可能认为在紧密循环中进行接收会以某种方式提高性能。
That condition normally is called a tight loop.
Are you sure that other code parts do not already use
poll()
(orselect()
) and expect the socket to be in non-blocking mode?Otherwise, then yes, the switch to the blocking mode is the simplest solution.
Best backward-compatible solution would have been before calling
recvfrom()
to usepoll()
to wait for the socket to become readable. That way ensures that other parts of the code would work precisely as before.For the case of
recvfrom()
no major difference is known to me.Could be a simple coding mistake. Or somebody might have thought that recv'ing in tight loop would somehow increase the performance.
将套接字设置为非阻塞总是更好,因为即使阻塞套接字有时也会变为就绪状态(当数据到达但有校验和错误并被丢弃时) - 即使没有数据可读取。因此使其成为
非阻塞
,通过轮询等待数据可用,然后读取。我认为这是主要优点。It is always better to make sockets as
nonblocking
because even a blocking socket becomes ready state sometimes (when data arrived but has checksum error and that is discarded) - even when there is no data to read. So make itnonblocking
, wait for the data availability through poll then read. I think this is the main advantage.