【问题标题】:Using Multi-threading for Slow Ms Interop使用多线程进行慢速 Ms 互操作
【发布时间】:2020-09-17 01:08:56
【问题描述】:

我有一个程序可以在单击按钮时创建两个 pdf 文件。它在 WinForms 中使用 Microsoft Office 互操作,文件创建过程如下;

  1. 用户在程序中处理某事
  2. 点击按钮
  3. 程序根据包含书签的模板创建 word 文件
  4. 写入书签及其表格
  5. 另存为 pdf 格式
  6. 关闭活动文档
  7. 关闭单词app
  8. 关闭子窗体并切换到另一个窗体

*单词 app 对用户不可见

它本身可以正常工作,但是完成这两个文件需要 8 秒,所以我尝试使用多线程,这样用户就不必等待,可以处理其他事情或继续执行步骤再 1 次。

但是,它会引发各种错误; COM、RPC 等,甚至在数据库连接中,我认为原因是因为有两个单独的线程在工作并且它使用相同的资源,所以某个时间点另一个可能关闭了另一个线程的资源正在使用/即将使用 所以我尝试使用join(),这样另一个线程可能会先完成它的工作,关闭其各自的资源,然后继续下一个。

这很好用,如果用户没有在另一个文件之后单击按钮来创建另一个文件(在用户完成第一步比预期更快的情况下)

join() 足以处理错误以及在后台处理文件创建,但是,我想处理文件创建紧随其后的情况,因为在这种情况下,然后看起来由于线程相互等待,进程是线性的,除了它会产生瓶颈,其中线程现在排队并阻塞主线程,这使用户等待 12 秒或更长时间。

我想知道/需要澄清的是;

  1. 我可以创建这两个使用 interop 的文件使用不同的资源,这样就不会产生错误吗?
  2. 我可以使用join() 的替代方法还是有替代方法,它不会阻塞 UI 线程或主线程?这样文件创建线程就在后台排队(仍然等待对方完成),而不会让用户等待。
  3. 我是否错误地使用了 join() 或最终消耗更多时间的线程?

这是我的代码;

用于创建文档的模块中的过程和公共变量:

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"

或者有时在不同时间或数据库连接过程中的一个案例中的任何地方。

我对多线程以及 Interop 女士还是比较陌生,所以如果我有任何误解,我会寻求帮助。代码在 VB 中,但我也可以理解 C#。非常感谢任何帮助/指导。谢谢!

【问题讨论】:

  • 你打电话给wordApp = New Word.ApplicationwordApp = CreateObject("Word.Application")有什么原因吗?

标签: c# vb.net multithreading winforms office-interop


【解决方案1】:
  1. 我不会尝试一次使用来自多个线程的任何 word/office 互操作。以我的经验,这些事情本身就足够不可靠,而不会引入可能的并发问题。但这可能取决于您到底在做什么。
  2. 线程和连接是处理多线程的相当老派的方法。较新的方法是使用Tasks and async/await。这可能有助于强制执行一致的排序,而不会阻塞主线程。
  3. 您通常应避免在从主线程执行时可能阻塞的任何方法,包括 .Join()。

我会考虑使用生产者/消费者模式。当用户单击按钮时,主线程将生成要处理的文档,而后台线程将消耗文档并进行处理。 blocking collection 通常可以用作两个线程之间的接口。

另一种选择是limited concurrency task scheduler。这使您可以安排任务进行处理,同时确保同时处理一个(或其他一些限制)。

【讨论】:

    【解决方案2】:

    我的建议是使用嵌入的 VBA 程序创建一个启用宏的模板,该程序执行所有书签和保存到 PDF。这将 Interop 调用减少为仅创建/处理单词 application 并使用传入的参数运行 VBA 过程。

    【讨论】:

      猜你喜欢
      • 2016-04-07
      • 1970-01-01
      • 1970-01-01
      • 2012-08-11
      • 2021-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多