Почему распараллеленный код не будет записываться в электронную таблицу Excel? - PullRequest
0 голосов
/ 19 мая 2018

Написание большого количества листов в электронной таблице 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 в 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))

Это не происходит с List.iter заменой PSeq.iter.

Мне не удалось воспроизвести это исключение в достаточно простом контексте, чтобы он представлял собой правильный вопрос SO, но я все равно был бы заинтересован в любых предложениях по его решению.

1 Ответ

0 голосов
/ 20 мая 2018

Похоже, код Microsoft.Office.Interop.Excel никогда не был предназначен для одновременного вызова из нескольких потоков. Вот вопрос, который кто-то задавал на форумах MS Office по поводу обновления в нескольких потоках (в C #).Я процитирую соответствующие части этого ответа здесь:

Использование многопоточности для поиска в нескольких таблицах заканчивается использованием сердца Excel - объекта Excel.Application, что означает, что потоки должныбыть в очереди для запуска по одному, лишая вас желаемого улучшения производительности для приложения.

[...]

Все это потому, что объектная модель Office непотокобезопасен.

Похоже, вы застряли с использованием непараллельного дизайна, если вы вызываете что-либо в пространстве имен Microsoft.Office.Interop.

Редактировать: У Аарона М. Эшбаха было замечательное предложение в комментариях: выполнить всю фоновую работу над несколькими потоками и использовать MailboxProcessor для актуальных обновлений электронной таблицы.Очередь сообщений MailboxProcessor автоматически сериализует операции обновления для вас, без дополнительной работы с вашей стороны.

...