【问题标题】:C#: Populating a UI using separate threadsC#:使用单独的线程填充 UI
【发布时间】:2011-02-17 15:29:42
【问题描述】:

我试图从我收到的应用程序中找出一些意义,以便追踪错误的来源。有一些代码(此处简化)创建了四个线程,这些线程依次填充主窗体上的列表视图。每个方法都从数据库中获取数据并从资源 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。”这让我想知道上面的代码是否以某种方式链接。

是否推荐使用这种填充 UI 的方法,如果不推荐,可能会导致哪些并发症?

更新:

我可能通过引用“表格”给出了一些错误信息。该应用程序是一个 Windows 窗体应用程序,但代码来自基于用户控件的插件应用程序。线程是在此控件公开的初始化方法中创建的。列表视图等也是此插件用户控件的一部分。

【问题讨论】:

  • 是您要填写此插件的 UI 控件,还是您希望填写另一个 UI 控件中的信息?
  • 用户控件更像是一个单独的插件应用程序,它包含许多列表视图,正在更新的正是这些。
  • 我明白了。你有这个插件UserControl的源码吗?
  • 是的,宿主容器和用户控件都属于我们,但负责它们的开发人员已不在公司。用户控件中的初始化方法由容器调用,正是该方法创建了填充列表视图控件的线程。
  • 我会建议也许尝试找出一种方法来调用 UI 线程,以便它可以填充控件,使用委托或任何我建议的解决方法。我的回答你清楚吗?

标签: c# .net multithreading user-interface


【解决方案1】:
  • 不要为此使用线程 - 如果您必须异步执行此操作,请在 THreadPool 上使用 WOrkItems。一般来说,应该为长时间运行的项目保留线程使用 - THreadPool 或新的 .NET 4.0 任务 API 更适合这种情况。

  • UI 元素只能从元素创建线程中进行命名。它是否“有效”取决于您使用的 .net 框架的版本,或者如果您破坏了它,该控件的真正含义是什么。

【讨论】:

【解决方案2】:

在您的线程方法中,例如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 在该主题上有一些改进,因为 Microsoft 一直在努力使并行性更易于开发人员使用多核处理器。

【讨论】:

    【解决方案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?

    【讨论】:

    • 我确实是指 Windows 窗体,特别是 Windows 窗体上的用户控件。我本来应该包含这些信息,现在已经更新了问题。
    【解决方案4】:

    永远不要从工作线程更新 UI。程序有时可能会工作,但这是未定义的行为。在程序初始化代码中加入这一行:

    Control.CheckForIllegalCrossThreadCalls = true;

    在此之后,每次错误的 UI 更新尝试都会失败,从而可以修复代码中的所有错误。

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 2012-01-28
        • 2012-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-17
        相关资源
        最近更新 更多