在C#中从线程更新UI的其他方法

时间:2012-03-22 15:16:18

标签: c# multithreading thread-safety ui-thread

我的应用程序在很大程度上依赖线程来处理非常大的数据。在处理完成时,需要更新UI。我知道并尝试使用BackgroundWorker's OnProgressChangedRunWorkerCompleted方法来更新UI。还使用UI线程的Invoke方法进行更新。在Win XP 32位和64位操作系统上,一切似乎都能正常工作。在Win Vista和Win 7(32位和64位)上,应用程序在使用Invoke方法更新UI时随机挂起。

Invoke的行为是否会在不同的Win OS上发生变化? 除了Invoke之外,还有哪些其他方法可以从线程更新UI?

由于

4 个答案:

答案 0 :(得分:2)

不确定出现了什么问题,但您可以随时运行System.Windows.Forms.Timer来定期更新GUI;如果需要,使用一些成员变量在线程之间传递原始数据。这不是最优雅的解决方案,但它可能会让您对悬挂的内容有不同的看法,因为线程更加独立,而不是依赖于后台线程Invoke主线程。

答案 1 :(得分:2)

  

Invoke的行为是否会在不同的Win OS上发生变化?

不应该,不。但是,线程问题可能以非常不可预测的方式实现。你可能有一个身份不明的问题。

  

除了Invoke之外,还有什么方法可以从线程更新UI?

使用InvokeBeginInvoke过度使用,尤其是在尝试向UI线程报告简单的进度信息时。如果您搜索我与该主题相关的一些答案,您将看到我一直在抨击这种方法。并且有充分的理由,因为它们使用这种技术有许多缺点。遗憾的是,BackgroundWorker仅使用此机制通过其ProgressChanged事件更新UI。

另一种方法是让您的工作线程将进度信息发布到共享变量中,并通过计时器定期对其进行UI线程轮询。以下是我通常的谈话要点,以证明这种方法优于编组技术。

  • InvokeBeginInvoke是昂贵的操作。
  • UI线程决定更新表单及其控件的时间和频率。
  • 它消除了ISynchronizeInvoke强加的UI和工作线程之间的紧密耦合。
  • 使用一堆编组操作不会有超出或超出UI消息队列的风险。
  • 您可以在工作线程上获得更多吞吐量,因为它不必像Invoke那样等待响应。

答案 2 :(得分:1)

您可以尝试使用Disokecher.Priority枚举作为参数的Invoke()或BeginInvoke()重载之一。如果您选择“背景”等参数,您应该会看到您的应用程序仍然具有响应性。然后,唯一的问题就是确保您以足够的速度为传入数据提供服务,而不会增加队列。

答案 3 :(得分:0)

另一种选择是完全放弃多线程。如果长时间运行的操作可以分解为块,请在GUI线程中执行,并在需要更新GUI时使用Application.DoEvents()

我曾经不喜欢使用该方法,因为它不仅可以更新GUI,还可以开始响应用户输入,启动计时器等,但最终它的安全性并不比使用后台线程更安全,后者允许GUI随时开始做任何事情。因此,在每次调用Application.DoEvents()之后,您可能需要检查_canceled或其他任何内容。 (我最终决定不喜欢这种方法的存在,因为它消除了线性执行顺序的保证,而不是使用它。)

当然,您会以这种方式失去多核支持,因此如果您尝试同时运行大量后台操作,则会影响性能。