如何在 C# 中同时使用进度条、后台工作程序、窗口窗体

本文关键字:后台 工作程序 窗体 窗口 | 更新日期: 2023-09-27 18:35:23

我遇到了一个问题,即我正在使用Backgroundworker 在进度条中显示我的工作进度。用于后台工作者的代码:-

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        Thread.Sleep(200);
        for (int i = 0; i <= 100; i++)
        {
           Delegate del= new DELEGATE(simulateHeavyWork);
           this.Invoke(del);
            backgroundWorker1.ReportProgress(i);
            if (backgroundWorker1.CancellationPending)
            {
                e.Cancel = true;
                backgroundWorker1.ReportProgress(0);
                return;
            }
        }
    }
    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
        percentLabel.Text = e.ProgressPercentage.ToString() + "%";
    }
    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled)
        {
            MessageBox.Show("Cancelled");
        }
        else
        {
            MessageBox.Show("Completed");
        }
    }

我已经在代码上创建了一个委托:-

public partial class Form1 : Form
{
    private delegate void DELEGATE();

    public Form1()
    {
        InitializeComponent();
    }
     private void simulateHeavyWork()
    {
        Thread.Sleep(100);
         ...lines of code to perform some search logs operation..
....
      }
 }

我想要实现的功能是进度条应该报告我的函数 simulateHeavyWork() 的进度,该函数实际上正在使用 UI 线程,因为它需要从我的表单控件中获取输入并更新它。

现在正在发生的问题是代码实际上正在调用 simulateHeavyWork() 并提供正在更新 UI 控件并且工作完成的输出。(注意:我在这里使用了委托以避免在 ui 线程上运行的错误交叉控件,因为我的函数需要使用 UI 控件。

完成后,它

开始更新进度条,这是错误的,看起来它一次又一次地调用 simulateHeavyWork,睡眠间隙 (100)。

如何在 C# 中同时使用进度条、后台工作程序、窗口窗体

user3222101,正如Andy之前所说,你正在连续运行simulateHeavyWork()。此外,通过调用 Invoke,您将在 UI 线程中运行此方法,这会导致 UI 线程中的额外睡眠。基本上,Invoke 使用使用它的控件的消息循环(泵)(在本例中为 Form1),并将委托放入 UI 线程的队列中以便执行。我认为这不是一个好的做法,因为 Sleep() 调用和 simulateHeavyWork() 方法中的耗时日志操作。

我希望,清楚地了解你的问题。我的建议是将耗时的日志操作与 UI 线程分开。不要将 UI 线程的宝贵时间花在缓慢而无聊的 I/O 操作上。从控件中获取值(在BackgroundWorker中使用 Invoke,我将在下面解释),在BackgroundWorker中执行任何您想要的操作并更新您的 GUI(再次使用 Invoke),而无需接触此类繁重任务的 UI 线程。

正如 Andy 建议的那样,您可以通过 RunWorkerAsync 参数传递数据,并且应该创建一个可以存储您需要的任何数据的类(因为它只接受一个参数)。但是,您可以随时使用 Invoke 从另一个线程中获取表单中的值。调用方法还返回委托中的值(请参阅下面链接中的示例),这使您有机会在窗体上获取控件的值。创建一个委托,该委托返回您为 RunWorkerAsync 创建的类类型的对象,并在 BackgroundWorker 线程中使用此值。请看一下这里的例子。

public static string GetTextThreadSafe(this TextBox box)
{
    return GetTextBoxText(box);
}

此外,示例使用 Func<...> 来返回值。

通过这种方式,您可以(在线程中)休眠一段时间(BackgroundWorker然后从控件中获取值(当前值)并执行任何您想做的事情(再次BackgroundWorker线程中)。我认为,这改进了您的代码。

从你的问题:"这是错误的,看起来它一次又一次地调用模拟沉重的工作与睡眠(100)的间隙。

当然叫。只需查看您的代码:

for (int i = 0; i <= 100; i++)
{
   Delegate del= new DELEGATE(simulateHeavyWork);
    this.Invoke(del);

所以你在这里给simulateHeavyWork打电话100次。而且由于您在simulateHeavyWork正文中输入了Thread.Sleep(100); - 呼叫之间的差距约为Sleep(100)