使用线程时GUI会冻结

5

我希望你能帮我解决问题。我的问题是关于一个类/图形用户界面的,其中有一个加载条被设置为跑马灯模式,以便在执行任务时向用户显示进度。

在我的GUI类中,构造函数的第一行我正在创建一个新的该类实例,然后进行如下操作:

    LoadingBar bar = new LoadingBar();
    Thread thread = new Thread(bar.Show);
    thread.Start();

然而,即使主程序线程在进行更加密集的操作,这个GUI仍然似乎会冻结,即使我使用后台工作器。我提到的方法有什么问题吗?如果有,我需要做出哪些改变呢?
谢谢。

WinForms应用程序?什么是LoadingBar控件? - acoolaum
这是一个Windows窗体应用程序,LoadingBar是我创建的一个类,其中包含一个进度条和一个标签,上面写着“正在加载!” - rik
将UI代码放在主线程中,将其他任何内容放在工作线程中。 - David Heffernan
请查看我的示例代码和运行此代码所需的步骤,以通过后台工作程序运行。 - Allen Rice
它不起作用是因为您启动的线程没有在消息循环中运行。这使得窗口处于僵死状态。需要使用Application.Run或Form.ShowDialog。但是,实际上应该反过来做。 - Hans Passant
4个回答

6

你需要改变你的方法。GUI需要保留在主线程中,而工作则在“工作线程”(通常是BackGroundWorker)中完成。然后工作线程向GUI报告,GUI再进行更新。


此外,如果他要继续使用线程,他必须使用Control.Invoke。 - acoolaum
是的,BackGroundWorker 更加适合。它有很好的事件处理机制。 - Vincent Vancalbergh
是的,他不应该继续使用Thread,BackgroundWorker非常容易使用,我简单概述了这个过程。 - Allen Rice

2

最好相反。在线程(或后台工作器)中进行密集工作,然后在主应用程序线程中显示等待屏幕。


1
你需要使用一个 BackgroundWorker。将其拖放到你的窗体上,点击 backgroundWorker1 并将 WorkerReportsProgress 属性设置为 True
然后进入事件(通过属性窗口),并附加处理程序:
  • DoWork,这是由进度条表示的所有工作。你将通过此方法“报告进度”,而后台工作者将确保在 UI 线程上调用 ProgressChanged。
  • ProgressChanged,这是根据进度和状态数据更新 UI 的地方。
DoWork 事件看起来像这样:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    var userState = new StateClass();

    while (working)
    {
        // TODO: do work here

        // update the state surrounding this task via userState
        userState.property = "some status";

        // report the progress so that backgroundWorker1_ProgressChanged gets called 
        this.backgroundWorker1.ReportProgress(percentComplete, userState);
    }

}

ProgressChanged事件的样子如下

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    // e.UserState contains the state data you passed to ReportProgress, 
    //   you have to cast it to the right type though, since its of type object
    var userState = (StateClass)e.UserState;
    int progress = e.ProgressPercentage;

    // TODO: report progress to the UI with the above variables
}

现在你所要做的就是通过调用this.backgroundWorker1.RunWorkerAsync()来告诉后台工作者开始工作。

0

我认为你没有在后台工作线程中完成所有的工作,否则GUI不会冻结。我们需要看到更多你的代码,或者你需要再次查看后台工作线程示例,以确定可能出了什么问题。

编辑

在报告进度调用之后添加一个System.Threading.Thread.Sleep(1);的调用。


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