从另一个线程更新进度条

3

我有一个在主线程上的Windows表单,还有另一个线程执行一些计算。我想从在另一个线程中进行的工作更新我的表单上的状态栏。最好的方法是什么?

到目前为止,我尝试的所有方法都使事情变得非常缓慢。我正在使用Visual Studio 2005。


你需要更具体地说明 - 是什么变慢了?你如何使用线程? - Nikolai Fetissov
3个回答

4
您可以使用类似于Control.Invoke的封送技术来在 UI 线程上执行委托,从而可以安全地操作 UI 元素,但是这种方法并不好。实际上,如果您只想更新简单的进度信息,那么这种方法非常糟糕。
迄今为止,最好的方法是:
  • 让您的工作者线程将进度信息发布到共享变量中。
  • 让您的 UI 线程通过适合您的间隔的 System.Windows.Forms.Timers 进行轮询。
以下是可能的代码示例。
public class Example : Form
{
  private volatile int percentComplete = 0;

  private void StartThreadButton_Click(object sender, EventArgs args)
  {
    StatusBarUpdateTimer.Enabled = true;
    new Thread(
      () =>
      {
        for (int i = 1; i <= 100; i++)
        {
          DoSomeWork();
          percentComplete = i;
        }
      }).Start();
  }

  private void StatusBarUpdateTimer_Tick(object sender, EventArgs args)
  {
    yourStatusBarPanel.Text = percentComplete.ToString() + "%";
    StatusBarUpdateTimer.Enabled = percentComplete < 100;
  }
}

这很有效的原因是:
  • percentComplete字段被声明为'volatile',确保其值可以从多个线程可靠地读取。
  • UI线程可以决定何时以及如何更新UI...这才是正确的方式!
  • 工作线程在继续之前不必等待来自UI线程的响应,这与Invoke的情况不同。
  • 它打破了UI和工作线程之间的紧密耦合,而Invoke会强加这种耦合。
  • 它更高效...相当高效。
  • 您可以在UI线程和工作线程上获得更高的吞吐量。
  • 不存在饱和UI消息队列的可能性,就像使用BeginInvoke的情况一样。
  • 您不必每次需要从工作线程更新UI时都要添加Invoke调用。

2

请确保只在主线程中更新用户界面,否则会出现问题。您可以通过调用Invoke来切换线程上下文。这里有一篇很好的文章(链接)介绍了这个问题。


1

你可以向主线程发送消息并让它更新进度条,但是你需要检查这些消息。你也可以像轮询函数一样做同样的事情。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接