【发布时间】:2016-06-17 10:43:05
【问题描述】:
我有一个长时间运行的机器学习程序,我在后台在 winforms 应用程序中使用所有内核并行运行。我会定期更新 UI 以报告进度。
机器似乎选择了相当随机的时间在 UI 线程上执行消息泵。有时我会在几分钟内没有收到更新,有时我每次发送一条消息都会收到一条消息。
我已经尝试了各种方法来使这个可靠,包括从后台线程进行标准调用,使用来自后台工作人员的进度报告,使用 UI 线程上的计时器来收集信息并显示它,减少最大线程数可以并行运行,弄乱线程优先级等。我发现可靠获取更新的唯一方法是将控制台添加到 winforms 程序并将进度输出到控制台。出于某种原因,这是 100% 可靠的,但它是一个真正的 hack,看起来很乱。
有人知道强制ui线程可靠更新的方法吗?
根据要求:这是复制错误的最基本代码。创建一个带有名为 label1 的标签的表单。该代码尝试每百万次迭代更新一次标签。
Module testmodule
delegate sub invokedelegate(txt as string)
Sub long_running_process()
Dim x(100000000) As Integer
Dim cnt As Integer
Dim syncobject As New Object
form1.Label1.Text = "started"
Parallel.ForEach(x, Sub(z)
'*** This just put in to make the processors do some work.
Dim p As New Random
Dim m As Double = p.NextDouble
Dim zzz As Double = Math.Cosh(m) + Math.Cos(m)
'*** This is the basic updating method.
SyncLock syncobject
cnt += 1
'*** Update every millionth iteration
If cnt Mod 1000000 < 1 Then
'**** This is how it is marshalled to the UI thead.
If Form1.InvokeRequired Then
Form1.BeginInvoke(New invokedelegate(AddressOf invokemethod), {cnt})
Else
Form1.Label1.Text = cnt
End If
End If
End SyncLock
End Sub)
Form1.Label1.Text = "Finished"
End Sub
Sub invokemethod(txt As String)
form1.Label1.Text = txt
End Sub
end module
【问题讨论】:
-
UI 线程不会选择 时不时地运行消息泵 - 这就是它所做的一切,一遍又一遍,一整天,每一天。如果您没有收到消息,那是因为您没有发送它们或 UI 线程太忙而无法及时处理它们。如果您确定要发送消息,请使用分析器来计算您的 UI 线程执行附加到每条消息的处理程序所需的时间。如果它比更新消息之间的时间长,那么您将累积消息积压并且事情将停止工作。 minimal reproducible example 会很有帮助。
-
另一种可能是资源争用——如果你的线程频繁分配内存,它们可能会独占内存管理器,如果你的 UI 处理程序也需要分配内存,它可能会等待线程提供轮到你了……再一次,没有任何代码来查看你实际在做什么,很难提供具体的建议。
-
您是否使用
BeginInvoke将您的消息发布回UI 线程消息泵?如果你这样做,我认为你不应该看到任何这样的问题。如果您发布用于编组 UI 更新的代码,这将有助于我们识别问题。 -
我添加了一个最小的完整且可验证的示例。
标签: .net vb.net multithreading winforms