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

Sol*_*lma 2 parallel-processing excel 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 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
Run Code Online (Sandbox Code Playgroud)

但是,替换以List.iter下面的注释行开头的行会弹出一个包含相同四个工作表的电子表格,但所有工作表都是空白的.

所以我的问题是:为什么不能用PSeq并行编写代码写入Excel?

备注:

本来我有一个不同的问题.也许是因为在我的应用程序中,当我尝试运行类似于上面的代码时,工作表更重,PSeq有一个例外

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))
Run Code Online (Sandbox Code Playgroud)

List.iter替换时不会发生这种情况PSeq.iter.

我无法在一个简单的上下文中复制这个异常是一个正确的SO问题,但我仍然对处理它的任何建议感兴趣.

rmu*_*unn 5

看起来Microsoft.Office.Interop.Excel代码从未被设计为一次从多个线程调用.以下是有人在MS Office论坛中询问有关在多个线程中进行更新的问题(在C#中).我将在这里引用该答案的相关部分:

使用多线程在多个工作表中进行搜索最终会使用Excel的核心--Excel.Application对象,这意味着线程需要排队等待一次运行一次,从而使您无法获得应用程序所需的性能改进.

[...]

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

如果你在Microsoft.Office.Interop命名空间中调用任何东西,看起来你仍然坚持使用非并行设计.

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

  • 您可以在多个线程上完成所有后台工作,并使用`MailboxProcessor`序列化电子表格的实际更新. (5认同)
  • @Soldalma - "非线程安全"并不意味着同时从多个线程运行该代码将始终失败,这只是意味着它不能保证始终成功.到目前为止它在较短的工作表上工作正常的事实并不意味着它将*始终*继续工作.您可能会遇到这样的情况,其他地方的CPU或I/O负载过重会导致较短的工作表线程在通常成功的位置失败,因为线程的时间以您无法预测或控制的方式发生变化.更安全序列化一切,恕我直言. (3认同)