Simon Urbanek在 r-devel 邮件列表中回答了这个问题。以下是他的回复副本以供将来参考:
是什么导致了这种差异?
时间限制只能在 R_ProcessEvents() 中检查,因此出于所有实际目的,它只能由调用 R_CheckUserInterrupt() 的可中断代码触发。现在,完全由前端来决定事件循环的方式。例如,R 的终端版本除了异步触发的输入处理程序之外没有其他需要担心的中断,因此它不需要进行任何轮询。Sys.sleep() 仅在输入处理程序上触发,因此如果您没有任何外部事件源挂钩作为输入处理程序,则没有理由处理任何事件,因此 Sys.sleep() 将看不到任何检查的理由时限。
我可以在终端 R 会话中设置什么来触发时间限制吗?
在 OS X 上,这实际上非常简单:quartz(); dev.off()
会成功的。原因是 Quartz 需要强制事件循环以便异步处理来自窗口的事件。它通过安装基于计时器的输入处理程序来实现。此处理程序将确保 Sys.sleep() 每 100 毫秒唤醒一次(您可以使用 QuartzCocoa_SetLatency 更改值),因此它将在该分辨率下超时:
> testlimit <- function(){
+ setTimeLimit(elapsed=3, transient=TRUE);
+ Sys.sleep(10);
+ }
> system.time(testlimit());
Error in Sys.sleep(10) : reached elapsed time limit
Timing stopped at: 0 0.001 10.001
> quartz(); dev.off()
null device
1
> testlimit <- function(){
+ setTimeLimit(elapsed=3, transient=TRUE);
+ Sys.sleep(10);
+ }
> system.time(testlimit());
Error in Sys.sleep(10) : reached elapsed time limit
Timing stopped at: 0.002 0.003 3.019
在 Linux 上,没有内置计时器,因此您必须添加一个输入处理程序来抢占 Sys.sleep()。如果您想要一个恒定的计时器,您可以简单地从 Quartz 中借用代码(查看 src/library/grDevices/src/qdCocoa.m 中的 QuartzCocoa_SetupEventLoop)或 CarbonEL 包。它实际上只是一个作为输入处理程序添加的管道,当您想要唤醒事件循环时,您可以在其中异步写入。在我的头上,我现在想不出 R 中的内置解决方案(尽管可以说 R 可能会在设置限制时自行安装处理程序......)。
但请注意,这实际上只是 Sys.sleep() 的一个特例。如果您实际运行 R 代码,则在评估期间(或在可中断的 C 代码中)会自动触发 ProcessEvents。
干杯,西蒙