我正在编写一个小型.NET(Windows窗体)应用程序,但我似乎无法使多线程正确。
应用程序有一个非常简单的逻辑:它显示一个主窗体,其中包含一些要填充的参数,一个用于开始处理的按钮和一个进度条;当用户点击按钮时,处理开始,程序运行时应更新进度条,处理时可以弹出一些MessageBox
es以显示警告/信息,当处理结束另一个{{1}时弹出结果。相当简单......但我似乎找不到正确处理控件更新的方法。
首先我尝试了简单的路线:在Button_Clicked事件中完成所有处理。这样做不顺利:它有效,但是在处理时表格变得完全没有反应,它根本不关心任何用户点击。我认为这是因为它实际上忙于处理事件,因此停止处理其他事件......所以我选择了多线程。
在按钮处理事件中,我启动了一个新的MessageBox
并让它进行处理,然后在其上调用Thread
; MSDN documentatin说Thread.Join
:“阻塞调用线程直到线程终止,同时继续执行标准COM和SendMessage抽取”(http://msdn.microsoft.com/en-us/library/95hbf2ta.aspx)。但似乎并非如此:在调用Thread.Join
之后,表单再次没有响应,直到线程终止。为什么是这样?我抛弃了Join的想法并立即从事件处理函数返回(禁用表单上的按钮以避免用户同时启动多个处理),但后来我遇到了另一个问题:我在尝试更新进度时开始遇到异常bar,运行时抱怨从一个不是它的所有者的线程调用了一个控件。
所以我最终选择Thread.Join
:我在表单中添加了一个,称为BackgroundWorker
方法,并使用RunWorkerAsync()
触发ReportProgress()
事件并处理它主窗体(避免跨线程控制更新问题)。我终于能够更新进度条......但是现在这是完全异步的:进度条实际上是在工作线程调用ProgressChanged
(甚至一秒!)之后的某个随机间隔更新的,这给出了特别难看的视觉效果。两个例子:
ReportProgress()
,然后ReportProgress()
显示有关当前处理步骤的一些消息,我可以看到进度条在消息框弹出后推进起来。MessageBox.Show()
事件会弹出最后RunWorkerCompleted
个结果,我可以看到它在屏幕上弹出,而进度条仍在填充其最后一步 我在这里缺少什么?
答案 0 :(得分:0)
到目前为止,处理WinForm(和WPF)应用程序异步的最佳方法是Reactive Extensions。要经历一段曲线但是一旦你得到它你将不会回去。
答案 1 :(得分:0)
添加Application.DoEvents()以更新需要它的UI。
答案 2 :(得分:0)
嗯,你已经尝试了很多东西。以下是一些提示,可能不适合您,但可以帮助您。
要克服“从其他线程更新控制”问题,您可以在控件上使用Invoke
或BeingInvoke
命令(我为此使用extension method)。
除此之外,我认为Kangkan的评论是合法的 - 使用MessageBox只会给你的问题带来痛苦,因为它是模态的,它阻止了UI的更新。你确定MessageBox是合适的 - 在继续之前你绝对需要用户输入吗?或者你只是需要通知他们?如果是后者,你应该考虑使用更像状态栏的东西,或者你在StackOverflow上获得的系统消息(想象一下,如果它们都是警报!)