非阻塞 I/O 问题
Non-blocking I/O issue
我对套接字上的异步 I/O 经验不多。最近我不得不实现一个 TCP 客户端,它将在一个单独的线程中连接到服务器并等待输入数据,并且以非阻塞方式等待输入数据,以便通过简单地从控制线程设置一些标志变量来立即终止线程。我的解决方案是设置套接字非阻塞并实现一个循环,如果 errno 是 E_WOULDBLOCK 则一遍又一遍地调用 recv 并在标志变量 运行 不是时退出循环'不要再设置了。是这样的:
while(run) {
if( -1 == recv(...))
{
if(errno == E_WOULDBLOCK)
continue;
else
break;
}
else
{
process_data(...);
}
}
问题是:这个解决方案在 CPU 用法方面有多好?使用 select/poll/epoll 会更有效吗?
还有一个问题:有什么方法可以让 select、read or connnect 立即从其他线程调用 return?
how good is this solution in terms of CPU usage?
忙等待效率很低
Will using select
/poll
/epoll
be more effective?
是的。
is there any way to make blocked select
, read
or connnect
return immediately
是,向阻塞的线程发送信号。有问题的函数将 return -1
和 errno
设置为 EINTR
。设置信号处理程序时,请注意编译器的实现如何处理 SA_RESTART
。
您提供的代码使用了紧密的轮询循环(即,它在收到输入之前不会阻塞,甚至在轮询之间等待)。由于 CPU 不断检查输入,因此效率可能非常低。通常,只有在输入可用之前的最长时间受到严格限制时,这样的轮询才适用;在绝大多数情况下,您应该阻止使用 select
或 epoll
.
之类的内容
如果您有一个线程在 select
(或类似的)上被阻塞,强制它从另一个线程恢复的一种简单方法是创建一个管道,将管道的读取端添加到select,并在您想要解锁 select 线程时从另一个线程写入管道。
假设您使用 C 语言编程并且拥有合适的内核版本,pselect
是正确的选择。
同时等待输入和信号是一个众所周知的问题,关于与此相关的问题和解决方案有很多很好的讨论。您可能会发现 this discussion 是一个好的开始。
The question is: how good is this solution in terms of CPU usage? Will
using select/poll/epoll be more effective?
非常效率低下。这可能是处理多个连接的最糟糕的方式。
考虑每个 recv
是一个系统调用:它需要上下文切换。上下文切换并不昂贵,但在高频循环中计算上下文切换可能会占用 CPU 的用量。
另外,如果你在一个电话和另一个电话之间睡觉,到 "soften the loop",你最终会在数据接收和处理之间付出延迟;联系越多,感觉就越高。
select
基本上告诉内核:"I want these sets of fds to be monitored by you and also be signaled as soon as something happens."。您的流程将进入等待状态,并在传递事件时准备就绪。同时,CPU 可能服务于其他进程。
您可以使用阻塞多路复用器并在单独的线程(可能来自线程池)中处理每个工作,这是非常有效的。但这取决于你在做什么。
And another one question: is there any way to make blocked select, read or connnect call return immediately from other thread?
嗯,是的。但这是你想要的吗?正如@alk 所说,您可以向他们发送信号。此外,对于 recv
和 connect
,您可能希望使用 select
/poll
或任何其他具有超时属性的多路复用器;不过,它可能更难编程。
我对套接字上的异步 I/O 经验不多。最近我不得不实现一个 TCP 客户端,它将在一个单独的线程中连接到服务器并等待输入数据,并且以非阻塞方式等待输入数据,以便通过简单地从控制线程设置一些标志变量来立即终止线程。我的解决方案是设置套接字非阻塞并实现一个循环,如果 errno 是 E_WOULDBLOCK 则一遍又一遍地调用 recv 并在标志变量 运行 不是时退出循环'不要再设置了。是这样的:
while(run) {
if( -1 == recv(...))
{
if(errno == E_WOULDBLOCK)
continue;
else
break;
}
else
{
process_data(...);
}
}
问题是:这个解决方案在 CPU 用法方面有多好?使用 select/poll/epoll 会更有效吗?
还有一个问题:有什么方法可以让 select、read or connnect 立即从其他线程调用 return?
how good is this solution in terms of CPU usage?
忙等待效率很低
Will using
select
/poll
/epoll
be more effective?
是的。
is there any way to make blocked
select
,read
orconnnect
return immediately
是,向阻塞的线程发送信号。有问题的函数将 return -1
和 errno
设置为 EINTR
。设置信号处理程序时,请注意编译器的实现如何处理 SA_RESTART
。
您提供的代码使用了紧密的轮询循环(即,它在收到输入之前不会阻塞,甚至在轮询之间等待)。由于 CPU 不断检查输入,因此效率可能非常低。通常,只有在输入可用之前的最长时间受到严格限制时,这样的轮询才适用;在绝大多数情况下,您应该阻止使用 select
或 epoll
.
如果您有一个线程在 select
(或类似的)上被阻塞,强制它从另一个线程恢复的一种简单方法是创建一个管道,将管道的读取端添加到select,并在您想要解锁 select 线程时从另一个线程写入管道。
假设您使用 C 语言编程并且拥有合适的内核版本,pselect
是正确的选择。
同时等待输入和信号是一个众所周知的问题,关于与此相关的问题和解决方案有很多很好的讨论。您可能会发现 this discussion 是一个好的开始。
The question is: how good is this solution in terms of CPU usage? Will using select/poll/epoll be more effective?
非常效率低下。这可能是处理多个连接的最糟糕的方式。
考虑每个 recv
是一个系统调用:它需要上下文切换。上下文切换并不昂贵,但在高频循环中计算上下文切换可能会占用 CPU 的用量。
另外,如果你在一个电话和另一个电话之间睡觉,到 "soften the loop",你最终会在数据接收和处理之间付出延迟;联系越多,感觉就越高。
select
基本上告诉内核:"I want these sets of fds to be monitored by you and also be signaled as soon as something happens."。您的流程将进入等待状态,并在传递事件时准备就绪。同时,CPU 可能服务于其他进程。
您可以使用阻塞多路复用器并在单独的线程(可能来自线程池)中处理每个工作,这是非常有效的。但这取决于你在做什么。
And another one question: is there any way to make blocked select, read or connnect call return immediately from other thread?
嗯,是的。但这是你想要的吗?正如@alk 所说,您可以向他们发送信号。此外,对于 recv
和 connect
,您可能希望使用 select
/poll
或任何其他具有超时属性的多路复用器;不过,它可能更难编程。