理解Promises包中异步任务的阻塞执行问题
promises A promise library for R 项目地址: https://gitcode.com/gh_mirrors/prom/promises
背景介绍
在R语言的异步编程中,promises包是一个强大的工具,它允许开发者以更优雅的方式处理异步操作。然而,当我们在非交互式环境中(如Rscript或R -e命令)使用promises时,可能会遇到一个常见问题:异步任务未能按预期执行完成。
问题现象
当使用promises包中的promise_all函数组合多个异步任务时,在交互式R会话中能够正常工作,但在非交互式环境下(如通过Rscript执行)会出现异步任务未完成就被终止的情况。这是因为R进程在函数执行完毕后立即退出,而不会等待异步任务完成。
解决方案
为了解决这个问题,我们需要一种机制来阻塞主进程,直到所有异步任务完成。以下是实现这一目标的几种方法:
基本阻塞实现
block_until_settled <- function(p) {
promise_resolved <- FALSE
p$finally(function(value) {
promise_resolved <<- TRUE
})
while(!promise_resolved) {
later::run_now(1)
}
}
这个函数通过不断检查promise状态来实现阻塞,直到promise被解决(resolved)或拒绝(rejected)。
带超时机制的阻塞实现
为了防止无限等待,我们可以添加超时机制:
block_until_settled <- function(p, timeout = 10) {
start_time <- as.numeric(Sys.time())
promise_resolved <- FALSE
p$finally(function(value) {
promise_resolved <<- TRUE
})
while(!promise_resolved) {
if (Sys.time() > start_time + timeout) {
stop("Timed out!")
}
later::run_now(0.5)
}
}
这种实现方式在超时后会抛出错误,但需要注意在交互式会话中,即使超时,异步任务仍会在后台继续执行。
使用示例
以下是一个完整的使用示例,展示了如何结合future_promise和阻塞机制:
buildDB_sarek <- function() {
library(future)
library(promises)
# 创建多个异步任务
csq_promise <- future_promise({
csq.vcf <- data.frame("a" = "a")
message("执行csq_promise任务")
return(csq.vcf)
})
info_promise <- future_promise({
info.vcf <- data.frame("a" = "a")
message("执行info_promise任务")
return(info.vcf)
})
# 组合多个promise
combined_promise <- promise_all(geno_promise, csq_promise, info_promise) %...>% {
# 处理结果
message("所有任务完成")
}
# 阻塞直到所有任务完成
block_until_settled(combined_promise)
}
注意事项
-
避免在Shiny或Plumber应用中使用:这种阻塞机制会干扰这些框架的事件循环机制,可能导致不可预知的行为。
-
集群环境考虑:在集群环境中使用时要特别注意资源管理,确保不会因为阻塞导致资源浪费。
-
性能影响:频繁检查promise状态会带来一定的性能开销,应根据实际情况调整检查间隔。
总结
通过实现阻塞机制,我们可以在非交互式环境中确保异步任务完成后再退出进程。这种方法特别适用于脚本执行、批处理作业等场景。然而,开发者需要根据具体应用场景权衡使用,并注意相关的限制和注意事项。
理解promises包的这种特性有助于我们更好地设计异步程序,特别是在需要将交互式开发成果部署到生产环境时,能够避免因执行环境差异导致的问题。
promises A promise library for R 项目地址: https://gitcode.com/gh_mirrors/prom/promises
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考