【问题标题】:Background Worker in external reference (DLL) locking main thread外部参考 (DLL) 锁定主线程中的后台工作人员
【发布时间】:2013-07-26 00:33:34
【问题描述】:

我几乎每天都使用后台工作人员,而且效果很好。今天我遇到了一个实例,虽然我需要将我的后台工作人员放在一个单独的项目中,然后是我正在运行的那个项目,因为我需要在我的解决方案中的两个不同项目中使用这个类。当我在 winforms 表单上测试编码时,它可以完美运行,在后台线程上处理我的编码。当我尝试从外部项目中引用此类时,我的所有编码似乎都运行得很好,但它似乎没有在后台线程上做任何事情,导致我的主窗口锁定。

有什么办法可以解决这个问题/在外部类中 ASYNC 调用的最佳做法是什么。

注意,我基本上创建了一个你称之为 start 的类,当数据准备好时会触发一个事件,所以我的外部项目并不是在等待方法完成。

提前致谢


我的例子

Solution ABC 有两个项目。项目 A 和项目 B。项目 A 是我的 WPF 应用程序,而 B 是我的 DLL。在项目 A 里面我有

Dim SmartCardData as new Solution.B()
SmartCardData.Start()

Project B has a sub

Public Sub Start()

   worker.workerConnect.RunWorkerAsync()

End Sub

Private Sub workerConnect_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles workerConnect.DoWork

'loop 10 seconds connecting to my device
e.Result = true

End Sub

Private Sub workerConnect_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles workerConnect.RunWorkerCompleted

  RaiseEvent Scanner_Connected()

End Sub

(抱歉,我无法将我的编码显示为代码,而不是纯文本)

【问题讨论】:

  • 您的外部项目是基于表单还是控制台?此外,您可以在调试器中查看线程和调用堆栈,这将有助于您了解外部应用的运行时行为。
  • 外部项目是一个 COM 对象,这绝对是锁定我的编码的原因。我正在寻找一种解决方案来调用 COM 对象上的方法,该方法现在不会锁定我,但运气不佳

标签: c# vb.net multithreading backgroundworker


【解决方案1】:

首先使用调试器。在 DoWork 事件处理程序上设置断点。当它中断时,使用 Debug + Windows + Threads 并验证它是否在工作线程上运行并且您看到列出的主线程。双击主线程并查看调用堆栈,确保它处于空闲状态并且没有在执行诸如等待 BGW 完成之类的操作。这是一个有保证的死锁。

下一个故障模式是难以诊断的。当您过于频繁地调用 ReportProgress 时,您可以冻结 UI 线程,即使使用后台工作程序也是如此。 UI 线程充斥着调用请求,不再执行其正常职责。就像绘画和响应输入一样。一切仍然正常运行,worker 和 UI 线程实际上正在运行,你只是看不到它。

这很容易发生,每秒报告超过一千次以上的进度是危险区域。这取决于 UI 线程需要在 ProgressChanged 事件处理程序中完成多少工作。诊断这是否是您的问题的最佳方法是在 ReportProgress() 调用之后添加 System.Threading.Thread.Sleep(45) 。这通常会减慢 worker 的速度,让 UI 线程有机会赶上。

您可以通过以对人眼有用的速度报告进度来解决此问题。以每秒 25 次更新的速度,除了模糊之外什么都看不到。收集 bgw 会生成一个像 List 这样的集合对象,因此您可以通过一次调用来更新 UI,例如 AddRange()。如果 BGW 生成结果的速度远远快于 UI 可以使用的速度,这可能仍然不够好,您将不得不跳过结果或人为地减慢工作人员的速度。

【讨论】:

  • 当我这样做时,我的主线程位于 ID 8312 上,断点位于 6707“工作线程”上,位置为“DoWork”。主线程只是在 VerifyAPIReadOnly() 上说它。至于progresschanged,我不为此做任何事情,因为我真的不关心发送任何进度。我只是在外部 DLL 中调用该函数,该函数启动一个 bg 工作程序。 bgworker_dowork 完成大约 10 秒的工作(调用另一个与读卡器交互的外部 DLL)。一旦做完工作,它就会传递我需要完成的数据,这会引发一个事件,将它传递给主线程。
  • 您的主线程应该是空闲的,在 .NET 消息循环代码内的 Application.Run() 上。按 F5,轻敲你的脚,Debug + Break All 并再次检查。如果你的程序没有空闲,它就会死锁。如果您无法解码,请在您的问题中发布堆栈跟踪。
  • 这是空闲的,但我用一个大循环替换了我的编码,它工作正常,所以它与我对读卡器的调用有关。我将对此进行更多调试,但会将您的标记为已回答,因为它帮助最大。谢谢
【解决方案2】:

后台工作者在主线程中处理它完成的代码,阻止 UI 响应。如果您不想在后台工作人员完成时更新 UI,请不要使用后台工作人员。

【讨论】:

  • 这很难分辨。我理解的后台工作人员的全部意义在于它在新线程上执行并且您的 UI 不会锁定。它对我来说一直都是这样。它打开一个线程,使用传递给它的 args 执行我需要的操作,然后在完成时返回我传递回的 args。
  • 什么是难以辨认的?当后台工作人员完成时,它会在主 UI 线程上执行 BackgroundworkerCompleted 事件,阻止所有其他主 UI 线程操作,直到调用 Application.DoEvents。
  • 我的 BackgroundworkerCompleted 仅在我的所有编码完成且锁定后触发。正在谈论几分之一秒来发送数据。问题是 DoWork 内部的编码将我的主线程锁定在主项目中......这永远不会发生,因为 DoWork 应该位于与主线程分开的线程上。
  • 您可能需要添加一些代码让我们看看出了什么问题。线程不会无缘无故地阻塞其他线程,除非同时从多个线程编辑共享资源。
【解决方案3】:

如果您的外部应用是基于控制台的,请改用 ThreadPool 来运行您的异步工作。 BackgroundWorker 委托需要一个消息泵才能在主线程上执行。

【讨论】:

  • 主项目是 WPF 应用程序,子项目是类 dll
猜你喜欢
  • 2014-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-15
相关资源
最近更新 更多