C#:使用单独的线程填充UI

时间:2010-05-10 12:02:40

标签: c# .net multithreading user-interface

我试图从我已经提交的应用程序中找出一些意义,以便追踪错误的来源。这里有一些代码(在此简化),它创建了四个线程,这些线程又在主窗体上填充列表视图。每个方法从数据库获取数据并从资源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的一部分。

5 个答案:

答案 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)

如果这样做是因为获取列表视图的值需要时间,然后在后台工作程序中获取值,然后使用主线程将数据绑定到列表视图。