Приложение Simple Shiny с invalidateLater () неправильно удаляет постоянные фоновые процессы
Я пытаюсь написать минимальное приложение Shiny, которое поддерживает постоянный внешний фоновый процесс. По причинам, характерным для полноразмерного варианта использования, я отслеживаю PID в текстовом файле, а не просто использую дескриптор. Когда я запускаю приложение, оно выглядит так:
Когда я нажимаю кнопку «Пуск», приложение создает фоновый процесс и записывает PID в текстовый файл. Реактивный контекст с многократно читает текстовый файл и показывает PID и статус.
Предполагается, что процесс будет выполняться, пока я не нажму кнопку «Стоп». Но менее чем через секунду после инициализации процесс завершается сам по себе.
Если я удалю
invalidateLater()
, процесс продолжается. В качестве альтернативы приложение работает, если я использую ручки вместо
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
РЕДАКТИРОВАТЬ: сборка мусора
Это из-за дескрипторов, собирающих мусор. Я могу продемонстрировать это с помощью 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
каждую секунду. Но в тот момент, когда я звоню
rm(px); gc()
в сеансе 1, сеансе 2 распечатки
FALSE
.
Теперь я вижу, что завершение сборки мусора является преднамеренной функцией
processx
: https://github.com/r-lib/processx#features . Думаю, это разумно для большинства ситуаций.
1 ответ
Процесс продолжится, если я установлю
cleanup
к
TRUE
в
callr::r_bg()
. Задача решена.