10

我想setTimeLimit用来中止在 n 秒后等待(空闲)的操作。下面是一个玩具示例,其中Sys.sleep是一个空闲的占位符调用:

testlimit <- function(){
  setTimeLimit(elapsed=3, transient=TRUE);
  Sys.sleep(10);
}

system.time(testlimit());

然而,这给出了不一致的结果。在 windows 和 r-studio server (linux) 上,通话在 3 秒win video后正确中止。但是,当我在linuxosx的终端会话中运行此程序时,直到之后才会触发超时,Sys.sleep()并且整个脚本需要 10 秒才能完成。

是什么导致了这种差异?我可以在终端 R 会话中设置什么来触发时间限制吗?我正在使用 Ubuntu 13.04 amd64、R 版本 3.0.1 RC 和 osx 10.8

4

2 回答 2

4

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。

干杯,西蒙

于 2013-05-16T19:46:01.847 回答
0

这是一个有趣的问题,我也遇到过。但是,如果您的函数执行子进程,例如通过system(). 在这种情况下,在整个过程执行之前没有时间检查(在我的情况下有时可能需要几个小时)。

但是,我为可能遇到相同问题的其他人找到了替代解决方案。我所做的是wait = FALSEsystem()调用中设置,然后强制评估脚本是否每 1 秒执行一次。它需要您向脚本添加一些外部副作用,例如在完成时创建一个临时文件。

Jeroen 的答案的编辑版本如下所示。

testlimit <- function() {
  setTimeLimit(elapsed = 3, transient = TRUE)
  system("sleep 10 && touch done", wait = FALSE)
  while (!file.exists("done")) {
    Sys.sleep(1)
  }
}
system.time(testlimit())
# Error in Sys.sleep(1) : reached elapsed time limit
# Timing stopped at: 0.008 0.005 3.009
于 2022-01-09T11:21:19.540 回答