我对套接字上的异步I / O没有多少经验。最近,我不得不实现一个TCP客户端,该客户端将以单独的线程连接到服务器,并以非阻塞的方式等待输入数据,以便通过简单地设置从控制线程变量的一些标志来立即终止线程。我的解决方案是设置套接字非阻塞并实现一个循环,如果errno是E_WOULDBLOCK则反复调用recv,并且当标志变量 run 不再设置时退出循环。就像那样:
while(run) {
if( -1 == recv(...))
{
if(errno == E_WOULDBLOCK)
continue;
else
break;
}
else
{
process_data(...);
}
}
问题是:这个解决方案在CPU使用方面有多好?使用select / poll / epoll会更有效吗?
还有一个问题:有没有办法让阻止选择,读取或连接立即从其他线程调用返回?
答案 0 :(得分:2)
这个解决方案在CPU使用方面有多好?
忙碌的等待是非常低效的。
使用
select
/poll
/epoll
会更有效吗?
是
有没有办法阻止
select
,read
或connnect
立即返回
是的,向被阻止的线程发送信号。相关功能将返回-1
,errno
将设置为EINTR
。设置信号处理程序时,请注意编译器实现如何处理SA_RESTART
。
答案 1 :(得分:2)
问题是:这个解决方案在CPU使用方面有多好?将 使用select / poll / epoll会更有效吗?
非常效率低下。这可能是处理多个连接的最糟糕方式。
考虑每个recv
是一个系统调用:它需要一个上下文切换。上下文切换并不昂贵,但在高频循环中计算上下文切换可能会充满CPU使用率。
此外,如果你在一个电话和另一个电话之间睡觉,为了“软化循环”,你最终会在数据接收和处理之间付出延迟;你拥有的联系越多,感觉就越高。
select
基本上告诉内核:“我希望你监视这些fds集合,并在发生某些事情时立即发出信号。”您的流程将进入等待状态,并在事件发布时准备就绪。同时,CPU可以服务于其他进程。
您可以使用阻塞多路复用器并在单独的线程(可能来自线程池)中处理每个工作,这非常有效。但这取决于你在做什么。
还有一个问题:有没有办法让其他线程立即阻止select,read或connnect call return?
嗯,是的。但是你想拥有什么?正如@alk所说,你可以给他们发信号。此外,对于recv
和connect
,您可能希望使用select
/ poll
或任何其他具有超时属性的多路复用器;但是,编程可能更难。
答案 2 :(得分:0)
您提供的代码使用严格的轮询循环(即,它不会阻止直到收到输入或甚至在轮询之间等待)。由于CPU不断检查输入,因此效率可能非常低。一般来说,这样的轮询只有在输入可用的最大时间紧密有限的情况下才适用;在绝大多数情况下,您应该阻止使用select
或epoll
等内容。
如果在select
(或类似)上有一个被阻止的线程,强制它从另一个线程恢复的一种简单方法是创建一个管道,添加读取端管道到选择,并在您想要取消阻止选择线程时从另一个线程写入管道。
答案 3 :(得分:0)
假设您使用C编程并拥有一个不错的内核版本,pselect
是可行的方法。
同时等待输入和信号是一个众所周知的问题,关于与之相关的问题和解决方案有很多很好的讨论。您可能会发现this discussion是一个良好的开端。