Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/gwt/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么并行化代码不会写入Excel电子表格?_Excel_Parallel Processing_F#_Excel Interop - Fatal编程技术网

为什么并行化代码不会写入Excel电子表格?

为什么并行化代码不会写入Excel电子表格?,excel,parallel-processing,f#,excel-interop,Excel,Parallel Processing,F#,Excel Interop,在Excel电子表格中编写许多工作表可能需要一段时间。将其并行化会有所帮助 此代码运行良好,它使一个Excel电子表格在屏幕上弹出,其中有四个工作表,分别名为Sheet1、1、2和3 open Microsoft.Office.Interop.Excel open FSharp.Collections.ParallelSeq let backtestWorksheets = [1..3] let app = new ApplicationClass(Visible = true) let

Excel
电子表格中编写许多工作表可能需要一段时间。将其并行化会有所帮助

此代码运行良好,它使一个
Excel
电子表格在屏幕上弹出,其中有四个工作表,分别名为
Sheet1
1
2
3

open Microsoft.Office.Interop.Excel
open FSharp.Collections.ParallelSeq

let backtestWorksheets = [1..3]

let app = new ApplicationClass(Visible = true) 

let workbook = app.Workbooks.Add(XlWBATemplate.xlWBATWorksheet)

let writeInfoSheet (worksheet: Worksheet) : unit =

    let foo i =
        let si = string i
        worksheet.Range("A" + si, "A" + si).Value2 <- "Hello " + si
    List.iter foo [1..10]

let wfm = [1, writeInfoSheet; 2, writeInfoSheet; 3, writeInfoSheet]
          |> Map.ofList

let adder (workbook : Workbook)
          (i        : int)
                    : unit =

    let sheet = workbook.Worksheets.Add() :?> Worksheet
    sheet.Name <- string i
    wfm.[i] sheet

List.iter (adder workbook) backtestWorksheets
//PSeq.iter (adder workbook) backtestWorksheets

[<EntryPoint>]
let main argv = 
    printfn "%A" argv
    0 // return an integer exit code
如果用
List.iter
替换
PSeq.iter
,则不会发生这种情况


我无法在一个足够简单的上下文中复制此异常,使其成为一个适当的SO问题,但我仍然对处理此异常的任何建议感兴趣。

看起来Microsoft.Office.Interop.Excel代码从来没有设计为一次从多个线程调用。在MS Office论坛中,关于在多线程中执行更新(在C#中)。我将在这里引用该答案的相关部分:

使用多线程在多个工作表中搜索最终会使用Excel的核心—Excel.Application对象,这意味着线程需要排队才能一次运行一个,从而剥夺了应用程序所需的性能改进

[……]

所有这些都是因为Office对象模型不是线程安全的

如果要调用
Microsoft.Office.Interop
命名空间中的任何内容,看起来您必须使用非并行设计


编辑:在评论中提出了一个很好的建议:在多个线程上完成所有后台工作,并使用
MailboxProcessor
对电子表格进行实际更新。MailboxProcessor的消息队列将自动为您序列化更新操作,而无需您额外的工作。

也许您可以同时编写单独的工作表,然后再合并它们?即使有办法让它同时工作在同一个文件中,我也不推荐它。(至于“为什么”它不受支持,这是一个你必须问微软的广泛问题。)我对F#一无所知,所以我不知道你将如何实现这一点,但“消息过滤器表明应用程序正忙”的解决方案是实现你自己的消息过滤器。请参阅:。请注意,运行筛选器的要求是STA线程。您可以在多个线程上执行所有后台工作,并使用
MailboxProcessor
序列化电子表格的实际更新。我不确定
Microsoft.Office.Interop.Excel
不能与并行化代码一起使用。该工作簿大约有20个工作表,其中只有三个在从
PSeq.iter
创建时生成异常。他们是唯一拥有数千个数据点图表的公司。因此,塔尔创建它们的代码必须是连续的(
List.iter
)。其他的被称为
PSeq.iter
。这样做可以节省大约10秒的时间(40秒->30秒)(我测试了从
List.iter
切换到
PSeq.iter
,在可以并行多次的组上,节省的时间是一致的)。@Soldalma-“非线程安全”这并不意味着同时从多个线程运行代码总是会失败,它只是意味着不能保证总是成功。事实上,到目前为止,在较短的工作表上,它对您的效果很好,但这并不意味着它将始终有效。您可能会遇到这样的情况:其他地方的CPU或I/O负载过大,导致较短的工作表线程在通常成功的情况下失败,因为线程的计时方式发生了无法预测或控制的变化。序列化所有内容更安全,IMHO。@rmunn-谢谢。我设法将时间从40秒减少到7秒,因为我每天都要跑这么多次,这是一个显著的区别。我是这段代码的唯一用户,所以我会一直这样做,直到出现问题,然后通过序列化罪犯工作表来解决问题。@rmun-
Aaron M.Eshbach的建议是个好主意,但看起来大部分时间都花在了电子表格的实际更新上,无论如何都会序列化。
Unhandled Exception: System.TypeInitializationException: The type initializer for '<StartupCode$Fractal13>.$Program' threw an exception. ---> System.AggregateException: One or more errors occurred. ---> System.Runtime.InteropServices.COMException: The message filter indicated that the application is busy. (Exception from HRESULT: 0x8001010A (RPC_E_SERVERCALL_RETRYLATER))