非阻塞 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 会更有效吗?

还有一个问题:有什么方法可以让 selectread 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 -1errno 设置为 EINTR。设置信号处理程序时,请注意编译器的实现如何处理 SA_RESTART

您提供的代码使用了紧密的轮询循环(即,它在收到输入之前不会阻塞,甚至在轮询之间等待)。由于 CPU 不断检查输入,因此效率可能非常低。通常,只有在输入可用之前的最长时间受到严格限制时,这样的轮询才适用;在绝大多数情况下,您应该阻止使用 selectepoll.

之类的内容

如果您有一个线程在 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 所说,您可以向他们发送信号。此外,对于 recvconnect,您可能希望使用 select/poll 或任何其他具有超时属性的多路复用器;不过,它可能更难编程。