3

我正在使用安装了本地服务器的 Shiny。

我的 Shiny 应用程序使用 system/system2/processx::run 运行繁重的本地程序。我正在同步运行它(等待=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)

当我使用 reprex 关闭浏览器窗口,然后尝试重新打开它时,该过程结束需要一些时间。我希望它或多或少立即可用。

PS我正在使用Linux;特定于系统的黑客很好。

4

1 回答 1

1

@PorkChop 的评论指向正确的方向。但是,我建议使用,processx::process而不是run因为它为我们提供了从 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)
于 2021-09-23T07:23:16.690 回答