我试图从我已经提交的应用程序中找出一些意义,以便追踪错误的来源。这里有一些代码(在此简化),它创建了四个线程,这些线程又在主窗体上填充列表视图。每个方法从数据库获取数据并从资源dll检索图形,以便直接填充图像列表和列表视图。
从我在这里阅读的内容(link)不应该从UI线程以外的任何线程更新UI元素,但这似乎有用吗?
Thread t0 = new Thread(new ThreadStart(PopulateListView1));
t0.IsBackground = true;
t0.Start();
Thread t1 = new Thread(new ThreadStart(PopulateListView2));
t1.Start();
Thread t2 = new Thread(new ThreadStart(PopulateListView3));
t2.Start();
Thread t3 = new Thread(new ThreadStart(PopulateListView4));
t3.Start();
错误本身是System.InvalidOperationException“无法将图像添加到ImageList”。让我想知道上面的代码是否以某种方式链接。
这是填充推荐用户界面的方法,如果没有,可能会产生哪些并发症?
更新
我可能通过提及“形式”给出了一些错误的信息。该应用程序是一个Windows窗体应用程序,但代码来自基于用户控件的插件应用程序。线程在由此控件公开暴露的初始化方法内创建。 listviews等也是此插件usercontrol的一部分。
答案 0 :(得分:6)
请勿使用线程 - 如果必须执行该异步,请在THreadPool上使用WOrkItems。对于长时间运行的项目,应该重新考虑线程使用 - 一个THreadPool或新的.NET 4.0任务API更适合这种方式。
UI元素只能从元素创建线程中获取。它“工作”与否“取决于您使用的.net框架的版本,或者如果您打破这个版本,控件的确是什么。
答案 1 :(得分:3)
正如其他人所说,你不能从创建它之外的任何线程更新你的UI。
如果线程想要更新UI,则需要在使用BeginInvoke创建它的同一线程上的UI控件上调用方法。
PS:我假设当你说UI时,你的意思是WindowsForms而不是WPF。我在WinForms中成功使用了上述解决方案。
更新:这里有几篇文章深入解释了这个概念: * Threading in Windows Forms * WinForms UI Thread Invokes: An In-Depth Review of Invoke/BeginInvoke/InvokeRequred
此外,来自SO的相关问题:In WinForms, why can't you update UI controls from other threads?
答案 2 :(得分:3)
在您的线程方法中,例如DoWork()的BackgroundWorker class,您需要通过委托方法来填充UI控件。然后,验证是否需要调用UI控件(InvokeRequired属性),然后在需要时调用它。
private delegate IList<MyObject> PopulateUiControl();
private void myThread_DoWork(object sender, DoWorkEventArgs e) {
PopulateUiControl myDelegate = FillUiControl;
while(uiControl.InvokeRequired)
uiControl.Invoke(myDelegate);
}
private IList<MyObject> FillUiControl() {
uiControl.Items = myThreadResultsITems;
}
这不是一个精确的工作代码,因为我不能花时间进行研究等,但这将使你走上成功的道路。
最后,我同意其他人的意见,试着在将来避免这样的事情,因为调试或揭露一些奇怪的行为会变得棘手。也许.NET 4在这个主题上有一些改进,因为微软努力使开发人员使用多核处理器变得容易并行。
答案 3 :(得分:2)
永远不要从工作线程更新UI。程序有时可能有效,但这是未定义的行为。将此行添加到程序初始化代码中:
Control.CheckForIllegalCrossThreadCalls = true;
在此之后,每次不正确的UI更新尝试都会失败,允许修复代码中的所有错误。
答案 4 :(得分:0)
如果这样做是因为获取列表视图的值需要时间,然后在后台工作程序中获取值,然后使用主线程将数据绑定到列表视图。