我想在每个doParallel
线程中执行一个tclTaskSchedule
计时器(来自tcltk2
包)。但是,以下最小代码
library(doParallel)
n <- detectCores()
cl <- makeCluster(n, outfile="out.log")
registerDoParallel(cl)
testfn <- function() print(paste("hello from", i))
foreach(i=1:n, .packages = c("tcltk", "tcltk2"), .verbose = T) %dopar% {
tclTaskSchedule(1000, testfn(), id = paste0("task", i), redo = 10)
}
stopCluster(cl)
导致错误(不是在控制台中打印,而是以out.log
打印)
Error in eval(expr, envir, enclos) : could not find function "testfn"
但是,从.verbose = T
参数中,我可以从控制台看到testfn
正在导出:
automatically exporting the following variables from the local environment:
testfn
实际上,使用 .export = "testfn"
调用 foreach
会导致相同的错误。
那么出了什么问题呢?
(我为什么要这样做?最后,我想以固定的时间间隔异步轮询多个数据源,并且每个数据源都有自己特定的轮询间隔)
我同意罗兰的观点,问题在于tclTaskSchedule
评估其参数的方式。 我的解决方案并不漂亮,但我通过使用 clusterExport
导出testfn
并在 foreach 循环中将i
分配给工作线程的全局环境来使其工作:
testfn <- function() print(paste("hello from", i))
clusterExport(cl, "testfn")
foreach(i=1:n, .packages = c("tcltk", "tcltk2"), .verbose = F) %dopar% {
i <<- i
tclTaskSchedule(1000, testfn(), id = paste0("task", i), redo = 10)
}
我可能也会把.noexport="testfn"
传给foreach,但这不是真的必要。
没有tclTaskSchedule
,您也会发现另一个问题: 并行化foreach
循环中的print
语句不会输出到交互式会话:
来自博客文章:
foreach 的一个问题是,它为循环的每次迭代创建新的 RScript 实例,这会阻止将状态消息记录到控制台输出中。
解决方法是创建输出日志文件:
cat("", file="log.txt")
testfn <- function() cat("hello from", i, "n", file="log.txt", append=TRUE)
foreach(i=1:n, .packages = c("tcltk", "tcltk2"), .verbose = T) %dopar% {
tclTaskSchedule(1000, testfn(), id = paste0("task", i), redo = 10)
}
然后可以使用bash
终端的tail -f log.txt
进行监控。