为什么使用select()而不是sleep()?

19

我正在学习关于iPhone音频的章节,遇到了一段我看不懂的代码:

while (aqc.playPtr < aqc.sampleLen) 
{
    select(NULL, NULL, NULL, NULL, 1.0);
}

(完整的代码示例在163-166页)。从我对代码的理解来看,音频正在另一个线程上进行处理,而while循环只是为了防止主线程在音频仍在处理时终止。
我不明白的是为什么要使用select()而不是sleep()
据我所了解,select()用于监视I/O的变化,并传递NULL没有任何有意义的作用。我已经使用sleep()运行了代码,并且它按预期工作。(我对低级POSIX的了解几乎不存在。)

2
吓人。我不知道人们仍然使用那种老套路。而且在一个看起来全新闪亮的架构上,应该有很多其他可以使用的“睡眠”手段。这样的编程人员/代码作者真是可耻。 - Jens Gustedt
8
当函数期望一个指向struct timeval指针作为参数时,传递一个double类型的参数并不像看起来那么可怕。请您放心,这并不会导致代码崩溃或其他严重问题。 - JeremyP
@JensGustedt 这实际上是一种非常安全的睡眠方式,因为在信号处理程序中可能无法使用sleep()。 - LubosD
3
@LubosD,很奇怪你回复了一个5年前的评论。我认为在信号处理程序中睡觉不是一个很好的主意。另外,自2001年以来,nanosleep 就已经被纳入了 POSIX 标准,已经有16年的历史了。它有一种明确定义的策略来处理信号,而 select 没有。一个尚未更新到该标准的 POSIX 系统应该被丢弃。 - Jens Gustedt
@JensGustedt 并不奇怪,你回复了一个5年前的帖子。有些事情一遍又一遍地发生。 - MauriceRandomNumber
6个回答

19

选择 select 允许精确的亚秒级等待,并且比 sleep 更具可移植性。还有其他等待方式,请参见 此问题

但是,select 的超时参数不应该是浮点数,而是指向结构体 timeval 的指针。我很惊讶您展示的代码甚至可以编译。此外:这个奇怪的条件 select 之后跟着一个无条件的 sleep(1)。在我看来毫无意义。


15
使用具有NULL rfds、wfds和efds的select()函数是一种惯用的方法,可以以可移植的方式进行亚秒级别的睡眠。

1
但是 timeout(第五个参数)不应该是指向 struct timeval 的(可能为空的)指针吗? - Matthew Flaschen
@MatthewFlaschen 在那种情况下,它将永远睡眠。 - Iharob Al Asimi

6

嗯,sleep(3)可以使用信号来实现。这取决于平台。

当您使用select(2)和poll(2)时,您知道不会涉及任何信号,这通常非常有用。例如,如果您正在使用alarm(2),则不应同时使用sleep(3),因为“混合调用alarm和sleep是一个坏主意”(根据手册。)

另外,select和poll在睡眠时提供毫秒级的精度,而sleep只以秒为单位提供精度。


3

当您在应用程序中使用SIGALRM信号并使用(u)sleep函数时,当SIGALRM发生时,程序会立即从sleep函数返回,因此最好的休眠方式是在select函数上等待。

struct timeval tv;

tv.tv_sec = 1;
tv.tv_usec = 1000;

do
{
  ret = select(1, NULL, NULL, NULL, &tv);
}
while((ret == -1)&&(errno == EINTR)); //select is interruped too

选择操作是否会调整 tv 以适应经过的时间?如果不是,那么您的等待循环将在每次 EINTR 发生时重新启动整个时间间隔。 - Patrick Schlüter

1
使用“select”而不是“sleep”的真正原因是精度,尤其是在音频或视频播放方面。在Linux、Windows或其他操作系统上,使用“select”可以获得更高精度的时间间隔来进行休眠,而不是使用“sleep”。
我很惊讶有很多人实际上不知道为什么。请停止对程序员/编写这种代码的人进行指责。

-6

没有理由这样做。也没有理由永远睡眠(Sleep())。一个程序应该总是期望至少一个事件 - 程序关闭请求。


我不明白那与任何事情有什么关系。关闭请求将导致一个SIGTERM信号,该信号由信号处理程序处理,无论是默认的还是其他方式。 - Matthew Flaschen
2
这既没有回答问题,也没有多少意义...有很多充分的理由去睡觉,特别是在嵌入式平台上(我猜 iPhone 也算一半)。 - Steven Schlansker
我同意Pavel的观点......几乎没有理由调用Sleep()(除非可能作为临时调试措施)。任何在Sleep()中被阻塞的线程在唤醒之前都无法干净地关闭,这使得关闭变得不必要地缓慢。(让Sleep()响应传入信号是一种解决方法,但混合信号和多个线程是一个令人讨厌的问题,最好避免)。更好的方法是使用事件驱动方法,并在可以通过传入事件解除阻塞的东西中进行阻塞,例如select()。 - Jeremy Friesner
这是错误的。在紧凑的事件循环中,大部分时间什么都不会发生,你可以仅仅做无事可做的事情就占用100%的CPU。添加一毫秒的sleep()可以在这些时期将CPU使用率降至接近0%。可能需要考虑权衡,并且select()或pselect()可能是更好的选择。但如果需要轮询,sleep()可能是您最好的朋友。 - milesvp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接