我们有客户端应用程序侦听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_set
和waitInterval
是本地对象),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_set
和timeout
参数的常见缺陷会被检查,但事实并非如此。
答案 0 :(得分:0)
实际上select()
没有任何问题,它正确地考虑了超时。测量错误 - 我测量的不是select()
调用本身,而是实际检查fd_set
并跳过select()
调用的包装函数,如果为空。所有时间<10000 usec都与select()
电话无关。
这个问题可能会被删除为不相关的。