繁体   English   中英

C# winforms,进度条将在按钮单击事件时冻结

[英]C# winforms, progress bar will freeze on button click event

因此,我的进度条在我的表单中为“选框”样式,因此它应该连续运行,但是当我单击按钮以显示进度时,它被冻结并且不会移动,但是如果我不隐藏进度条一开始它会正常运行。

我只希望它在单击按钮时出现,对此有什么想法吗?

    public Form1()
    {
        InitializeComponent();
        progressBar1.Hide();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        progressBar1.Show();
        DrawingHandler MyDrawingHandler = new DrawingHandler();
        MyDrawingHandler.GetConnectionStatus();
            try
            {
                Operation.RunMacro("shaftCheck.cs");
                DrawingEnumerator SelectedDrawings = MyDrawingHandler.GetDrawingSelector().GetSelected();
                if (SelectedDrawings.GetSize() == 0)
                {
                    MessageBox.Show("Error, no drawings to export.");
                }
                else
                {
                    Operation.RunMacro("ExportShaft2.cs");
                }
            }
            catch (Exception)
            {
                MessageBox.Show("Error");
            }
    }
}

最可能的原因是您在 UI 线程上执行某种缓慢的操作。

大多数情况下只有一个 UI 线程,这是唯一允许更新 UI 的线程,并且一次只能做一件事。 如果您使用 UI 线程进行某种处理,它就不能做其他事情,例如更新您的 UI。

解决方案是将所有处理密集型操作放在后台线程上。 例如Task.Run 但是您需要确保任何调用的代码都不会触发任何 UI 更新。

如果您只是将进度条作为对话框的一部分,则相当容易。 但这会导致用户在您进行处理时能够做其他事情,因此您需要非常小心所有代码都是线程安全的。

更安全的选择通常是显示阻止用户做任何其他事情的模式对话框。 这大大降低了线程安全问题的风险。 通常它看起来像这样:

            var progressForm = new MyProgressForm();
            Task.Run(MyWorkerMethod)
                .ContinueWith(t => progressForm.DialogResult = DialogResult.OK, TaskScheduler.FromCurrentSynchronizationContext());
            progressForm .ShowDialog();

进度条中是否有另一个属性更适合您的需求。 Winforms Progress Bar Visible 在这篇文章中是 true false看起来您可以使用 Visibility 属性更改可见性。

您的程序只有一个可以更新 UI 的线程。 如果你让这个线程忙于做其他事情,那么你的 UI 将不会更新,也不会响应操作员的输入。

void LongRunningMethod() {...}

private void OnButton1_Clicked(object sender, ...)
{
    this.progressBar1.Visible = true;
    this.LongRunningMethod();
    this.progressBar1.Visible = false;
}

在执行 LongRunningMethod 时,UI 不会响应:进度条没有更新,UI 不会响应用户输入。

有两种常用的方法来解决这个问题。

  • 老式的:使用BackGroundWorker ,并让这个 BackGroundWorker 执行 LongRunningMethod。 BackGroundWorker 完成后发送事件。 您可以使用该事件再次隐藏进度条。
  • 现代:使用异步等待。 让一个单独的任务执行 LongRunningMethod。

我不会讨论 BackGroundWorker。 如果你了解事件处理,你就会明白它的要点。 我将改为描述 async-await。

真正帮助我理解 async-await 的是对 Eric Lippert 的采访 在中间某处搜索异步等待。

在这次采访中,Eric Lippert 将 async-await 与做早餐的厨师进行了比较。 每当厨师必须等待某事,例如一壶水烧开时,他不会无所事事,不,他会环顾四周,看看是否可以做其他事情,例如在烤面包机中插入面包。 他不等面包烤好,就开始炒鸡蛋。 后来他回到沸水里泡茶,或者回到烤面包机里拿烤面包。

Async-await 类似:每当您的线程必须等待另一个进程完成时,例如写入文件、从数据库或从 Internet 读取,您可以让线程空闲等待,直到其他进程完成任务。 你也可以让线程做其他事情,比如更新进度条和监听用户输入。 这称为异步等待。

每当您的线程看到等待时,它不会空闲等待,而是向上调用堆栈并执行代码,直到调用者有等待,再次向上调用堆栈并执行代码,直到线程看到等待,等等。

稍后,当文件写入完成,或者数据库数据被提取,或者互联网数据被下载时,线程(或者更准确地说:线程池中的任何线程)将在等待之后执行代码。

为了能够使用 async-await,请执行以下操作:

  • 每当您的程序必须等待另一个冗长的过程时,例如我上面给出的示例,请找到该过程的异步版本:WriteAsync / ReadAsync 等
  • 定义你的异步方法。 使用 Async 终止方法的标识符是很常见的用法:ReadFileAsync、GetProductsAsync 等。这将使得也有可能具有非异步版本:ReadFile、GetProducts。
  • 返回Task<TResult>而不是TResult 返回Task而不是void
  • 这条规则有一个例外:事件处理程序总是返回void :没有人可以等待事件处理程序的结果
  • 在异步方法内部调用 WriteAsync / ReadAsync 等。在需要异步操作的结果之前不要等待。 在您的异步方法返回之前等待
  • 如果返回是Task<TResult> ,则等待的结果是TResult 如果您 await Task ,则返回无效。

因此,如果您需要从文件中读取文本行:

private async Task<List<string>> ReadFileAsync(string fileName)
{
    using (TextReader textReader = new StreamReader(fileName))
    {
        List<string> lines = new List<string>();
        string line = await textReader.ReadLineAsync();
        while (line != null)
        {
            // a line has been read, add it to the lines, and read the next
            lines.Add(line);
            line = await textReader.ReadLineAsync();
        }
    }
}

因此,如果操作员按下按钮来读取文件并异步处理读取的行:

private async Task ProcessLinesAsync(IEnumerable<string> linesToProcess) {...}

private async Task ReadAndProcessFileAsync()
{
    string fileName = this.GetFileName();
    List<string> lines = await this.ReadFileAsync(fileName);
    await this.ProcessLinesAsync(lines);
}

private async void OnButton1_Clicked(object sender, ...)
{
    this.ShowProgressBar();
    await this.ReadAndProcessFileAsync();
    this.HideProgressBar();
}

请注意,事件处理程序返回void而不是Task

ShowProgressBar 是一种方法,它会在使用this.progressBar1.Visible = true; .

使用其他线程进行冗长的计算

到目前为止,UI 线程必须等待另一个进程完成。 如果您需要在保持 UI 响应的同时进行一些长度计算,也可以使用 Async-await。 使用Task.Run让线程池中的可用线程之一进行冗长的计算。 当你需要结果时等待它:

private MyResult DoLengthyCalculations()
{
    // Do your lengthy calculations here:
    DrawingHandler MyDrawingHandler = new DrawingHandler();
    MyDrawingHandler.GetConnectionStatus();
        try
        {
            Operation.RunMacro("shaftCheck.cs");
            // etc
}

private async Task<MyResult> DoLengthyCalculationsAsync()
{
    MyResult result = await Task.Run( () => DoLengthyCalculations();
    return result;
}

顺便说一句:这是一个有异步和非异步方法的示例:调用者可以决定他想使用哪一个。

事件处理程序:

private async void OnButton1_Clicked(object sender, ...)
{
    this.ShowProgressBar();
    await this.DoLengthyCalculationsAsync();
    this.HideProgressBar();
}

其他任务开始后继续处理

如果您可以做一些有用的事情而不是等待任务完成,请不要等待。 尽可能晚地等待:仅当您需要结果时,并且在返回之前。

async Task DoMyWorkAsync()
{
    // start reading the file. Don't await, while the file is being read,
    // you can do other things:
    Task<List<string>> taskReadFile = this.ReadFileAsync();

    // because you didn't await, you are free to do something else as soon as
    // the thread sees an await:
    this.DoSomethingElse();

    // you can even start a second task:
    Task<MyResult> taskLengthyCalculations = this.DoLengthyCalculationsAsync();

    // as soon as you need the results, await for it:
    List<string>> readLines = await taskReadFile;
    this.ProcessReadLines(readLines);
    ...

如果您愿意:等待两个任务完成:

    Task[] allTasks = new Task[] {taskReadFile, taskLengthyCalculations};
    await Task.WhenAll(allTasks);

    // you can access the return value using property Result:
    List<string>> readLines = taskReadFile.Result;
    MyResult lengthyCalculationsResult = taskLengthyCalculations.Result;
    this.Process(readLines, lengthyCalculationsResult);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM