为什么select()不尊重超时,特别是在多线程中

时间:2014-12-01 12:32:52

标签: c multithreading sockets select

我们有客户端应用程序侦听UDP多播源并处理传入数据。它可移植,可在Windows和Linux上运行。主处理循环使用select()来等待数据,通常是一个或两个UDP非阻塞套接字:

while(!stopRequested)
{
   fdset io;
   FD_ZERO(&io);
   FD_SET(sock, &io);

   timeval waitInterval = { 0 };
   waitInterval.tv_usec = 10000; // 10 milliseconds

   int r = select(sock + 1, &io, NULL, NULL, &waitInterval); 
   if(r == 0) // Process timeout
   else // Data or error processing
}

代码工作得很好,但是当没有数据可用时,超时准确性存在问题。 我们测量了在保证空闲的几秒钟内没有实际花费在select()内的时间(没有数据发送),分布是这样的:

<1 usec     : 170 time(s)
<2000 usec  : 1 time(s)
<10000 usec : 11973 time(s)
<12000 usec : 6558 time(s)
<15000 usec : 64 time(s)
<20000 usec : 47 time(s)

没有错误,select()总是返回0.所以我们可以看到,当select()几乎立即返回时,有几种情况(170次),而不等待任何超时。

所以问题是为什么在几种情况下不遵守超时? Windows(Win7 x64)和Linux(CentOS / RHEL6.0 x64)都获得了类似的结果。

此外,使用多线程时情况变得更糟。当2个线程正在执行上面的代码时(同一个套接字调用select(),但fd_setwaitInterval是本地对象),select()内的时间分布是这样的(对于每个线程):

<1 usec     : 13800827 time(s)
<10 usec    : 1639 time(s)
<100 usec   : 8660 time(s)
<1000 usec  : 16 time(s)
<12000 usec : 768 time(s)
<15000 usec : 39 time(s)

看起来select()几乎从不尊重超时,但在并发呼叫中立即返回0。

对这种令人困惑的行为有什么解释吗?不会重新初始化fd_settimeout参数的常见缺陷会被检查,但事实并非如此。

1 个答案:

答案 0 :(得分:0)

实际上select()没有任何问题,它正确地考虑了超时。测量错误 - 我测量的不是select()调用本身,而是实际检查fd_set并跳过select()调用的包装函数,如果为空。所有时间<10000 usec都与select()电话无关。

这个问题可能会被删除为不相关的。