如何解决带有 invalidateLater() 的简单 Shiny 应用程序错误地删除了持久的后台进程
我正在尝试编写一个最小的 Shiny 应用程序,它维护一个持久的外部后台进程。出于全尺寸用例的特定原因,我在文本文件中跟踪 PID,而不是仅使用 processx
句柄。当我启动应用程序时,它看起来像这样:
当我按下“开始”按钮时,应用程序会创建一个后台进程并将 PID 记录在一个文本文件中。带有 invalidateLater()
的响应式上下文会重复读取文本文件并显示 PID 和状态。
该进程应该一直运行,直到我单击“停止”。但在初始化后不到一秒钟,该进程自行退出。
如果我删除 invalidateLater()
,进程会继续运行。或者,如果我使用 processx
句柄而不是 ps
和文本文件,应用程序也能工作,但这对于我的用例来说还不够。
应用代码
library(callr)
library(ps)
library(shiny)
library(tools)
ui <- fluidPage(
actionButton("start","start"),actionButton("stop","stop"),textoutput("status")
)
server <- function(input,output,session) {
file <- tempfile()
observeEvent(input$start,{
px <- r_bg(function() Sys.sleep(Inf))
writeLines(as.character(px$get_pid()),file)
})
observeEvent(input$stop,{
pid <- as.integer(readLines(file))
if (pid %in% ps_pids()) {
ps_kill(ps_handle(pid))
}
})
output$status <- renderText({
invalidateLater(100)
if (file.exists(file)) {
pid <- as.integer(readLines(file))
paste(ifelse(pid %in% ps_pids(),"running","stopped"),pid)
}
})
}
shinyApp(ui = ui,server = server)
会话信息
R version 4.0.3 (2020-10-10)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7
Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
attached base packages:
[1] tools stats graphics Grdevices utils datasets methods
[8] base
other attached packages:
[1] shiny_1.6.0 ps_1.5.0 callr_3.5.1
loaded via a namespace (and not attached):
[1] Rcpp_1.0.6 magrittr_2.0.1 xtable_1.8-4 R6_2.5.0
[5] rlang_0.4.10 fastmap_1.1.0 jquerylib_0.1.3 htmltools_0.5.1.1
[9] ellipsis_0.3.1 yaml_2.2.1 digest_0.6.27 lifecycle_1.0.0
[13] processx_3.4.5 later_1.1.0.1 sass_0.3.1 promises_1.2.0.1
[17] rsconnect_0.8.16 cachem_1.0.4 mime_0.10 compiler_4.0.3
[21] bslib_0.2.4 jsonlite_1.7.2 httpuv_1.5.5
编辑:垃圾收集
这是因为垃圾收集的 processx
句柄。我可以用 2 个 R 会话来证明这一点。会话 1 创建一个后台进程。
px <- r_bg(function() Sys.sleep(Inf))
px$get_pid()
#> [1] 8252
会话 2 循环检查从会话 1 产生的后台进程。
library(ps)
while(TRUE) {
print(8252 %in% ps_pids())
Sys.sleep(1)
}
会话 2 开始时每秒打印 TRUE
。但是当我在会话 1 中调用 rm(px); gc()
时,会话 2 打印 FALSE
。
我现在看到垃圾回收的终止是 processx
: https://github.com/r-lib/processx#features 的一个故意特性。我想在大多数情况下都是合理的。
解决方法
如果我在 cleanup
中将 TRUE
设置为 callr::r_bg()
,该过程会继续进行。问题解决了。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。