使用foreach的dopar调用render_site时出现YAML output重复错误求助
解决render_site并行执行时YAML output重复的问题
我之前也碰到过一模一样的坑!你说的情况太典型了:用普通for循环跑render_site()完全正常,一换成foreach的%dopar%并行就报Duplicate map key: 'output'的错,而且换成render()就完全没问题——核心问题其实出在render_site()的一个隐藏行为上:它会自动修改磁盘上的_site.yml配置文件!
当多个并行进程同时调用render_site()时,它们都会去读写同一个_site.yml文件,多个进程同时往里面写入output相关配置,自然就会出现重复键的冲突。而render()不会修改这个配置文件,所以并行时不会有任何问题。
下面给你两种靠谱的解决方案:
方案一:给每个并行任务分配独立工作目录
每个进程在自己的专属目录里操作,复制一份配置文件和模板文件进去,彻底隔离文件操作,避免互相干扰。修改后的代码如下:
pacman::p_load(data.table, rmarkdown, knitr, kableExtra, dplyr, doParallel) # 预先读取模板和配置内容 info_rmd <- readLines("test.rmd", encoding = "UTF-8") yml_content <- readLines("site.yml", encoding = "UTF-8") fun <- function(r) { # 创建当前任务的独立工作目录 task_dir <- paste0("task_", r) dir.create(task_dir, showWarnings = FALSE) # 将模板和配置文件复制到任务目录 write(info_rmd, file = file.path(task_dir, "test_.Rmd"), encoding = "UTF-8") write(yml_content, file = file.path(task_dir, "_site.yml"), encoding = "UTF-8") # 切换到任务目录执行render_site original_wd <- getwd() setwd(task_dir) render_site("test_.Rmd", encoding = "UTF-8", quiet = TRUE) setwd(original_wd) # 可选:清理临时文件,如果你不需要保留中间Rmd文件 # file.remove(file.path(task_dir, "test_.Rmd")) # 如果不需要保留整个任务目录,可以用unlink(task_dir, recursive = TRUE) } # 并行执行部分 no_cores <- 3 cl <- makeCluster(no_cores, type = "SOCK") registerDoParallel(cl) res <- foreach(r = 1:10, .combine = c, .inorder = TRUE) %dopar% { library(rmarkdown) fun(r) } stopCluster(cl)
方案二:直接传入内存中的配置对象,绕过磁盘文件
render_site()其实支持通过site_config参数直接传入内存中的配置列表,不需要读取磁盘上的_site.yml。这样每个进程都用自己的配置对象,不会去修改磁盘文件,从根源上避免冲突:
pacman::p_load(data.table, rmarkdown, knitr, kableExtra, dplyr, doParallel, yaml) # 预先读取模板和解析配置为列表 info_rmd <- readLines("test.rmd", encoding = "UTF-8") site_config <- yaml::yaml.load_file("site.yml", encoding = "UTF-8") fun <- function(r) { temp_rmd_path <- paste0(r, "test_.Rmd") write(info_rmd, file = temp_rmd_path, encoding = "UTF-8") # 调用render_site时直接传入site_config,不读取磁盘文件 render_site(temp_rmd_path, encoding = "UTF-8", quiet = TRUE, site_config = site_config) file.remove(temp_rmd_path) } # 并行执行部分 no_cores <- 3 cl <- makeCluster(no_cores, type = "SOCK") registerDoParallel(cl) res <- foreach(r = 1:10, .combine = c, .inorder = TRUE) %dopar% { library(rmarkdown) library(yaml) fun(r) } stopCluster(cl)
这两种方法都能解决你的问题,选哪种看你的需求:如果需要保留每个任务的站点文件,方案一更方便;如果只是生成结果不想保留太多目录,方案二更简洁。
内容的提问来源于stack exchange,提问作者Geek
相关产品推荐
相关产品推荐

