Как завершить все процессы по окончании работы приложения 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)
Другие вопросы по тегам