Как завершить все процессы по окончании работы приложения Shiny?
Я использую Shiny с установленным локальным сервером.
Приложение My Shiny запускает тяжелую локальную программу с использованием system / system2 / processx::run. Я запускаю его синхронно (wait=T). Если пользователь закроет окно браузера Shiny, я бы хотел, чтобы тяжелая программа закончилась. Если пользователь повторно откроет окно браузера, я хочу, чтобы приложение Shiny снова было готово к выполнению локальной программы.
Как этого добиться?
Когда я использую system / system2 / processx::run, кажется, что приложение ждет завершения тяжелой программы и не останавливает ее при закрытии.
Реплекс:
library(shiny)
library(processx)
ui <- fluidPage(
actionButton("runBtn", label="Run a program that consumes many resources") ,
)
server <- function(input, output, session) {
observeEvent(input$runBtn,
run("sleep", "240"))
}
shinyApp(ui, server)
Когда я закрываю окно браузера с помощью REPEX, а затем пытаюсь снова открыть его, это занимает несколько минут. Я хочу, чтобы он был доступен немедленно.
1 ответ
Комментарий @PorkChop указывает в правильном направлении. Однако я бы рекомендовал использовать
processx::process
а не потому, что он предоставляет нам методы для управления запущенным процессом из R. См.
?process
. (
run
кстати тоже основан на классе процесса.)
Основная проблема здесь в том, что процесс запускается синхронно (
wait=TRUE
) блокирует сеанс R. Соответственно
onStop
не сработает, пока R не вернет контроль. Следовательно, вы не можете ничего запустить после закрытия окна браузера, потому что блестящий сеанс продолжает работать до тех пор, пока внешняя программа не завершится, и R не сможет закрыть блестящий сеанс.
В конце сеанса приведенный ниже код проверяет, жив ли асинхронно запущенный процесс, и при необходимости убивает его (проверено только на Windows).
library(shiny)
library(processx)
ui <- fluidPage(
actionButton("runBtn", label="Run a program that consumes many resources"),
actionButton("stopSession", "Stop session")
)
server <- function(input, output, session) {
myProcess <- NULL
observeEvent(input$stopSession, {
cat(sprintf("Closing session %s\n", session$token))
session$close()
})
observeEvent(input$runBtn,
{
if(Sys.info()[["sysname"]]=="Windows"){
writeLines(text = c("ping 127.0.0.1 -n 60 > nul"), con = "sleep.bat")
myProcess <<- process$new("cmd.exe", c("/c", "call", "sleep.bat"), supervise = TRUE, stdout = "")
} else {
myProcess <<- process$new("sleep", "60", supervise = TRUE, stdout = "")
}
myProcess$wait() # wait for the process to finish
})
onStop(function(){
cat(sprintf("Session %s was closed\n", session$token))
if(!is.null(myProcess)){
if(myProcess$is_alive()){
myProcess$kill()
}
}
})
}
shinyApp(ui, server)
Относительно различных функций обратного вызова сеанса см. Этот связанный пост .
Как и просили здесь, процесс заключен в
reactiveVal
:
library(shiny)
library(processx)
ui <- fluidPage(
actionButton("runBtn", label="Run a program that consumes many resources"),
actionButton("stopSession", "Stop session")
)
server <- function(input, output, session) {
myProcess <- reactiveVal(NULL)
observeEvent(input$stopSession, {
cat(sprintf("Closing session %s\n", session$token))
session$close()
})
observeEvent(input$runBtn,
{
if(Sys.info()[["sysname"]]=="Windows"){
writeLines(text = c("ping 127.0.0.1 -n 60 > nul"), con = "sleep.bat")
myProcess(process$new("cmd.exe", c("/c", "call", "sleep.bat"), supervise = TRUE, stdout = ""))
} else {
myProcess(process$new("sleep", "60", supervise = TRUE, stdout = ""))
}
# myProcess()$wait() # wait for the process to finish
})
onStop(function(){
cat(sprintf("Session %s was closed\n", session$token))
if(!is.null(isolate(myProcess()))){
if(isolate(myProcess()$is_alive())){
isolate(myProcess()$kill())
}
}
})
}
shinyApp(ui, server)