Использование многопоточности для медленного взаимодействия с MS - PullRequest
1 голос
/ 29 мая 2020

У меня есть программа, которая создает два файла PDF при нажатии кнопки. Он использует взаимодействие Microsoft office в WinForms, и создание файла происходит следующим образом:

  1. Пользователь работает над чем-то в программе
  2. Нажимает кнопку
  3. Программа создает слово файл на основе шаблона, в котором есть закладки
  4. Записывает как в закладки, так и в свою таблицу
  5. Сохраняет его как pdf
  6. Закрывает активные документы
  7. Закрывает приложение Word
  8. Закрывает дочернюю форму и переключается на другую

* приложение Word не отображается для пользователя

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

Однако при этом возникают различные ошибки; COM, RP C и др. c. даже в соединениях с базой данных, и я думаю, причина в том, что, поскольку есть два отдельных потока, которые работают и используют одни и те же ресурсы, в какой-то момент другой может закрыть ресурсы, которые другой поток использует / собирается использовать Поэтому я попытался использовать join (), чтобы другой поток мог сначала завершить sh свою работу, закрыть соответствующие ресурсы, а затем перейти к следующему.

Это работает нормально, ЕСЛИ пользователь не нажмите кнопку, чтобы создать еще один файл сразу за другим (в сценарии, когда пользователь завершает первый шаг быстрее, чем ожидалось)

join() достаточно для обработки ошибок, а также для работы с создание файлов в фоновом режиме, однако я хочу иметь дело со сценарием, когда создание файла выполняется сразу после другого, потому что в этом сценарии ios кажется, что процесс является линейным из-за потоков, ожидающих друг друга, помимо этого создает узкие места, в которых потоки теперь находятся в очереди и блокируют основной поток, который заставляет пользователя ждать 12 секунд или более.

Я хочу знать / уточнить следующее:

  1. Могу ли я создать эти два файла, которые использует interop использовать разные ресурсы, чтобы не создавать ошибок?
  2. Могу ли я использовать или есть альтернатива join(), в которой он не блокирует поток пользовательского интерфейса или основной поток? Чтобы файл, создающий потоки, просто выстраивался в фоновом режиме (все еще ждет друг друга, чтобы завершить sh), не заставляя пользователя ждать.
  3. Я неправильно использовал join () или потоки, которые в итоге съедают больше time?

Вот мой код;

Процедура и переменные publi c в модуле для создания документов:

Public tsThread As Thread
Public psThread As Thread

Sub SaveDoc(docType,someArgs)
        Dim wordApp = New Word.Application
        Dim templateBookmarks As Word.Bookmarks
        Dim templateName As String
        Dim template As New Word.Document
        wordApp = CreateObject("Word.Application")

        Select Case docType
            Case "Type1"
                templateName = "SampleType.docx"
                template = wordApp.Documents.Add(templatePath & templateName)
                templateBookmarks = template.Bookmarks

                templateBookmarks.Item("bookmarkInWord").Range.Text = "Foo"
                template.Tables(1).Cell(msWordRow, 1).Range.Text = "Value in cell 1"   
            Case "Type2"
                ‘Same thing, just different values and template
            Case "Type3"
        End select
        template.SaveAs2(savePath & saveName, Word.WdSaveFormat.wdFormatPDF)
        wordApp.ActiveDocument.Close(Word.WdSaveOptions.wdDoNotSaveChanges)
        wordApp.Quit()
End Sub

Процедура в дочернем форма при нажатии кнопки:

        If Not IsNothing(tsThread) Then
            If tsThread.IsAlive Then
                tsThread.Join()
            End If
        End If
        If Not IsNothing(psThread) Then
            If psThread.IsAlive Then
                psThread.Join()
            End If
        End If
        tsThread = New Thread(Sub() SaveDoc(docType1,someArgs))
        tsThread.Start()

        If Not IsNothing(tsThread) Then
            If tsThread.IsAlive Then
                tsThread.Join()
            End If
        End If
        psThread = New Thread(Sub() SaveDoc(docType2,someArgs))
        psThread.Start()

        If records = maxRecords Then
            If psThread.IsAlive or tsThread.isAlive Then
                tsThread.Join()
                psThread.Join()
                Dim fooThread = New Thread(Sub() SaveDoc(docType3,someArgs))
                fooThread.Start()
            End If
        End If
   SwitchForm("Child Form for Step 1")
   End Sub

Ошибки, т.е. без использования join(), обычно встречаются в следующих частях кода:

wordApp.ActiveDocument.Close(Word.WdSaveOptions.wdDoNotSaveChanges)

или в

wordApp.Quit()

Или в закладках

templateBookmarks.Item("bookmarkInWord").Range.Text = "Foo"

Или иногда где угодно внутри одного из кейсов в разное время или в процедурах подключения к базе данных .

Я все еще относительно новичок в многопоточности, а также в Ms Interop, поэтому я обращаюсь за помощью, если у меня есть какие-либо неправильные представления. Код находится на VB, но я тоже могу понять C#. Любая помощь / руководство приветствуется. Спасибо!

Ответы [ 2 ]

2 голосов
/ 29 мая 2020
  1. Я бы не пытался использовать какое-либо взаимодействие слова / офиса из более чем одного потока одновременно. По моему опыту, эти вещи и без того достаточно ненадежны, без возникновения возможных проблем с параллелизмом. Но это может зависеть от того, что именно вы делаете.
  2. Поток и соединение - это довольно старый способ обработки нескольких потоков. Более новый метод - использовать Задачи и async / await . Это может помочь обеспечить согласованный порядок без блокировки основного потока.
  3. Обычно вам следует избегать любых методов, которые могут блокироваться при выполнении из основного потока, включая .Join ().

Я бы подумал об использовании шаблона производитель / потребитель. Основной поток будет создавать документы для обработки, когда пользователь нажимает кнопку, в то время как фоновый поток потребляет документы и выполняет обработку. Блокирующая коллекция обычно может использоваться в качестве интерфейса между двумя потоками.

Другой альтернативой является планировщик задач с ограниченным параллелизмом . Это позволяет планировать задачи для выполнения обработки, обеспечивая одновременную обработку только одного (или некоторого другого ограничения).

0 голосов
/ 29 мая 2020

Я предлагаю создать шаблон с поддержкой макросов со встроенными процедурами VBA, которые делают все закладки и сохранение в PDF. Это сокращает вызовы Interop до простого создания / удаления приложения word и запуска процедуры VBA с переданными параметрами.

...