【问题标题】:Unreliable GUI update in multithreaded C# program多线程 C# 程序中不可靠的 GUI 更新
【发布时间】:2013-12-18 15:21:15
【问题描述】:

我有一个程序在一个窗口内的其他线程中运行一系列方法,让用户知道使用状态栏发生了什么。状态栏更新在设置状态栏然后刷新 GUI 的主线程中。有几个串联的代码块,每个看起来像这样:

Thread do1Thread = new Thread(Class.Method);
do1Thread.Start();
// inform user
this.status.Text = "Doing stuff 1...";
// update GUI
Utility.RefreshGUI();
// join thread
do1Thread.Join();

有时状态栏确实会更新,但通常会停留在第一个状态,直到最后显示最后一个状态。偶尔会坚持“准备好”。这是默认设置。

请注意,其中两个块需要几秒钟,因此应该有时间进行更新。此外,该程序是用 C# (Mono) 编写的,使用 GTK# 作为 GUI。

如何确保 GUI 更新以反映更改?

【问题讨论】:

  • 您在上面放置的代码块是否发生在 UI 线程上?如果在 UI 线程上执行 .Join() 是您的问题。
  • 大家好,我得了重感冒,等我好了,我会试试这些建议。感谢所有帮助。

标签: c# multithreading mono gtk#


【解决方案1】:

问题在于 Join() 调用阻塞了阻塞所有窗口消息的 UI 线程。

您可以使用 BackgroundWorker 并在 RunWorkerCompleted 调用中执行 Join 之后的任何代码吗?

【讨论】:

  • 我有点不确定为什么在这里使用Join() 是个问题。 Join 导致当前线程等待调用它的线程完成,对吗?因此,由于下一次 GUI 更新要等到 加入之后才会发生(只有两个异步线程,GUI 和进程线程),所以此时不​​应阻塞 GUI。我是不是误会了什么?
  • BTW 在 GTK# 中没有任何 BackgroundWorker
  • @Razick 你误会了,你可能已经改变了 GUI 用来显示的数据值,但是如果函数永远不会返回到消息泵,它就永远不会有机会用新信息重新绘制屏幕。您必须将控制权返回给消息泵,而不是使用Join 阻塞,这样才能发生绘制事件。然后,您可以使用Event 表示您的任务已完成,并让事件开始下一步,而不是阻止Join
  • 正如 Scott 所说,您正在更改 UI 控件的值,但会阻止消息泵,因此控件不会拾取绘制消息,因此它们不会更新。如果 BackgroundWorker 不可用,Event 将按照 Scott 所说的那样工作,只需确保您在 Event 中所做的任何 UI 更改都会调用回 UI 线程。
【解决方案2】:

您需要将更新消息发送到 UI 线程,调用 invoke 而不是直接属性

this.status.BeginInvoke((Action)(() => this.status.Text = "Something happen"));

【讨论】:

  • 运行时出现错误:Error CS1061: Type Gtk.Label' does not contain a definition for BeginInvoke' and no extension method BeginInvoke' of type Gtk.Label' could be found (are you missing a using directive or an assembly reference?)
  • @Razick 看看this post
  • @Razick,我已将转换添加到 Action 抱歉,BeginInvoke 方法需要 Delegate,因此需要直接转换..请尝试转换。另请注意,如果您提供的代码在 UI 线程中,Join 将阻止它并且不会发生更新
  • 我用 Action 试过了,是的,它在 UI 线程中,所以这一定是问题所在。
  • 第一个线程必须在第二个线程开始之前完成,因为第二个线程使用第一个线程的数据。
【解决方案3】:

我发现在主线程中更新控件的最佳方法是设置一个委托来更新并从其他线程调用它。

【讨论】:

  • 我会看看那个。我还不知道如何使用委托,所以可能需要一段时间。
【解决方案4】:

你必须使用可观察和可观察的模式。

已编辑:

划分逻辑并查看部分代码确实更好。

这是一个在现实世界中如何使用的例子。 Pattern

【讨论】:

  • 请多努力一点。这可以是一条评论,但对于答案来说有点太短了......
【解决方案5】:

您能否检查一下您是否正在使用 StatusStrip 控件? 如果是这样,您的代码看起来像直接设置状态条控件的文本

this.status.Text = "Doing stuff 1...";

所以它不会在状态条中反映为文本。在这种情况下,您必须放置一个工具条标签,并且需要设置其文本。

请查看here的帖子

【讨论】:

  • 问题在于他阻塞了 UI 线程,而不是他没有正确更改文本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-29
  • 1970-01-01
  • 1970-01-01
  • 2011-04-30
相关资源
最近更新 更多