使用%doPar%并行生成R Markdown报告时的异常问题如何解决?
问题描述
使用R Markdown批量生成报告时,单线程(%do%)或逐个运行完全正常,但改用%doPar%并行执行时会出现以下异常:
- 偶尔运行正常
- 生成的报告文件名不同,但内容完全重复
- 偶尔触发
pandoc document conversion failed with error 1转换失败错误
正常运行代码(%do%版本)
library(tidyverse) library(parallel) library(doParallel) OutputFolder <- "c:\\temp\\test\\out" result_foldername <- "Now" ServersInDB <<- c("server1.ru", "server2.ru") cores=detectCores(logical = FALSE) cl <- parallel::makeCluster(cores-1) # 避免占用全部核心 registerDoParallel(cl) render_all_obj <- function(MachineName, OutputFolder, result_foldername) { library(rmarkdown) render(input = "c:\\temp\\test\\proj\\Report.RMD", output_file = paste0(MachineName, ".html"), output_dir = file.path(OutputFolder, result_foldername), params = list(MachineName = MachineName) ) } foreach(MachineName = ServersInDB) %do% { render_all_obj(MachineName, OutputFolder, result_foldername) } parallel::stopCluster(cl)
报错代码(%doPar%版本)
library(tidyverse) library(parallel) library(doParallel) OutputFolder <- "c:\\temp\\test\\out" result_foldername <- "Now" ServersInDB <<- c("server1.ru", "server2.ru") cores=detectCores(logical = FALSE) cl <- parallel::makeCluster(cores[1]-1) # 避免占用全部核心 registerDoParallel(cl) render_all_obj <- function(MachineName, OutputFolder, result_foldername) { library(rmarkdown) render(input = "c:\\temp\\test\\proj\\Report.RMD", output_file = paste0(MachineName, ".html"), output_dir = file.path(OutputFolder, result_foldername), params = list(MachineName = MachineName) ) } foreach(MachineName = ServersInDB) %dopar% { render_all_obj(MachineName, OutputFolder, result_foldername) } parallel::stopCluster(cl)
对应的RMD文件内容
--- output: html_document: toc: true dev: 'svg' number_sections: true toc_depth: 2 toc_float: true theme: cerulean toc_collapsed: true self_contained: true mathjax: NULL params: MachineName: "ServerName" # 要分析的服务器名称 --- ```{r , echo=FALSE, include=FALSE, results='hide'} MachineName <- params$MachineName
title: "My report is about: r MachineName"
--- # 解决方案 ### 1. 解决并行临时文件冲突 并行时多个进程共享默认临时目录,pandoc生成的中间文件会互相覆盖,这是内容重复和转换失败的核心原因。 **修改`render_all_obj`函数,为每个进程分配独立临时目录**: ```r render_all_obj <- function(MachineName, OutputFolder, result_foldername) { library(rmarkdown) # 创建进程专属临时目录 temp_dir <- file.path(tempdir(), MachineName) dir.create(temp_dir, recursive = TRUE, showWarnings = FALSE) # 指定render使用独立临时目录 render(input = "c:\\temp\\test\\proj\\Report.RMD", output_file = paste0(MachineName, ".html"), output_dir = file.path(OutputFolder, result_foldername), params = list(MachineName = MachineName), intermediates_dir = temp_dir, knit_root_dir = temp_dir ) # 清理临时目录(可选) unlink(temp_dir, recursive = TRUE) }
2. 优化集群初始化,确保资源同步
并行集群默认不会自动传递主环境的包和变量,易导致进程资源加载异常。
修改集群初始化代码,显式导出变量并预加载包:
cores=detectCores(logical = FALSE) cl <- parallel::makeCluster(cores-1) # 导出必要变量到集群节点 parallel::clusterExport(cl, c("OutputFolder", "result_foldername", "render_all_obj")) # 在集群节点预加载rmarkdown包 parallel::clusterEvalQ(cl, library(rmarkdown)) registerDoParallel(cl)
3. 移除全局变量依赖,改用显式参数传递
原代码使用<<-定义全局变量ServersInDB,并行时可能出现变量同步问题。
修改代码,使用本地变量并显式导出:
# 移除全局变量赋值,改用本地变量 ServersInDB <- c("server1.ru", "server2.ru") # foreach中显式导出所需资源 foreach(MachineName = ServersInDB, .export = c("render_all_obj", "OutputFolder", "result_foldername")) %dopar% { render_all_obj(MachineName, OutputFolder, result_foldername) }
4. 限制并行进程数,避免资源竞争
过多并行进程会抢占系统资源,导致pandoc转换失败。
适当减少并行进程数:
# 至少保留1个核心给系统运行 cl <- parallel::makeCluster(max(1, cores-2))
内容的提问来源于stack exchange,提问作者Maxim
相关产品推荐
相关产品推荐

