【问题标题】:One Background Worker Thread and Multiple Progress Bars一个后台工作线程和多个进度条
【发布时间】:2012-07-28 13:31:54
【问题描述】:

我想向 WinForm 添加多个进度条,以监控嵌套循环中的三个可能较长的进程。我推测我应该只能使用一个后台工作线程,以下实现是否可以接受:

这是我正在完成工作的班级:

class Class1
    {
        public static void UpdatePB(BackgroundWorker worker)
        {
            for (int i = 1; i <= 10; i++)
            {
                for (int ii = 1; ii <= 10; ii++)
                {
                    for (int iii = 1; iii <= 10; iii++)
                    {
                        Thread.Sleep(10);
                        worker.ReportProgress(iii, "PB3");
                    }
                    Thread.Sleep(10);
                    worker.ReportProgress(ii, "PB2");
                }
                Thread.Sleep(10);
                worker.ReportProgress(i, "PB1");
            }
        }

这里是 backgroundWorker ProgressChanged 事件:

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        // Change the value of the ProgressBar to the BackgroundWorker progress.
        switch (e.UserState.ToString())
        {
            case "PB1":
                progressBar1.Value = e.ProgressPercentage;
                this.label1.Text = e.ProgressPercentage.ToString();
                break;
            case "PB2":
                progressBar2.Value = e.ProgressPercentage;
                this.label2.Text = e.ProgressPercentage.ToString();
                break;
            case "PB3":
                progressBar3.Value = e.ProgressPercentage;
                this.label3.Text = e.ProgressPercentage.ToString();
                break;
        }

        Application.DoEvents();
    }

【问题讨论】:

  • 运行时会发生什么?结果是你想要的吗? (我不这么认为)。请更具体地说明您想要实现的目标。
  • 是的,它工作正常,但我想确保这是一种适当且标准的做法。基本上,我想在我正在调用的长时间运行的方法中监控三个不同的步骤,并且我想在不同的线程上运行进度条更新,以便更频繁地更新它们。

标签: c# multithreading progress-bar backgroundworker


【解决方案1】:

好的,经过数小时的搜索,我找到了这个LINK,它让我能够弄清楚我做错了什么。基本上,我正在做的是在 BackgroundWorker 线程上运行进度条,同时仍在主 UI 线程上运行进程。我意识到这不是我应该做的,所以我现在在 BackgroundWorker 线程上运行所有内容。此外,您将在我的代码示例中看到,我需要维护两个单独的进度条并使用状态信息更新一个富文本框。我能够很容易地实现这个功能。这是我的最终解决方案:

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace BackgroundWorkerThreadExample
{
    public partial class Form1 : Form
    {
        public delegate void ProgressUpdatedCallaback(ProgressUpdatedEventArgs progress);
        BackgroundWorker bw = new BackgroundWorker();

        public Form1()
        {
            InitializeComponent();
            bw.WorkerReportsProgress = true;
            bw.WorkerSupportsCancellation = true;
            bw.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
        }

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            DatabaseProcessor.ProgressUpdated += new DatabaseProcessor.ProgressUpdatedEvent(ProgressUpdated);
            DatabaseProcessor.GetData();
        }

        private void backgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
            bw.Dispose();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            bw.RunWorkerAsync();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (bw.IsBusy == true)
            {
                bw.CancelAsync();
            }
            bw.Dispose();
        }

        private void ProgressUpdated(ProgressUpdatedEventArgs progressUpdated)
        {
            if (InvokeRequired)
            {
                Invoke(new ProgressUpdatedCallaback(this.UpdateProgress), new object[] { progressUpdated });
            }
            else
            {
                UpdateProgress(progressUpdated);
            }
        }

        private void UpdateProgress(ProgressUpdatedEventArgs args)
        {
            ProgressBar pb = new ProgressBar();
            Label lb = new Label();

            if (args.Message == "")
            {
                if (args.PBNum == 1)
                {
                    pb = progressBar1;
                    lb = label1;
                }
                else if (args.PBNum == 2)
                {
                    pb = progressBar2;
                    lb = label2;
                }

                if (pb.Maximum != args.Total)
                {
                    // initial setup
                    pb.Minimum = 0;
                    pb.Maximum = args.Total;
                    pb.Style = ProgressBarStyle.Continuous;
                }

                pb.Value = args.Processed;

                if (args.Total > 0)
                {
                    double progress = args.Processed / (args.Total * 1.0);
                    lb.Text = progress.ToString("P2");
                }
            }
            else
            {
                this.richTextBox1.Text += args.Message;
                //Goto last line
                this.richTextBox1.SelectionStart = this.richTextBox1.Text.Length;
                this.richTextBox1.ScrollToCaret();
            }
            //Application.DoEvents();
        }
    }

    public static class DatabaseProcessor
    {
        public delegate void ProgressUpdatedEvent(ProgressUpdatedEventArgs progressUpdated);
        public static event ProgressUpdatedEvent ProgressUpdated;

        public static void GetData()
        {
            int total = 126;
            Random randomGenerator = new Random();
            for (int i = 0; i < total; i++)
            {
                // Do some processing here
                double delay = (double)randomGenerator.Next(2) + randomGenerator.NextDouble();
                int sleep = (int)delay * 1000;
                System.Threading.Thread.Sleep(sleep);
                RaiseEvent(1, total, i + 1);
                RaiseEvent(0, 0, 0, string.Format("Processing Item {0} \r\n", i + 1));

                for (int ii = 0; ii < total; ii++)
                {
                    // Do some processing here
                    double delay2 = (double)randomGenerator.Next(2) + randomGenerator.NextDouble();
                    int sleep2 = (int)delay2 * 10;
                    System.Threading.Thread.Sleep(sleep2);
                    RaiseEvent(2, total, ii + 1);
                }
            }
        }

        private static void RaiseEvent(int pbNum, int total, int current, string message = "")
        {
            if (ProgressUpdated != null)
            {
                ProgressUpdatedEventArgs args = new ProgressUpdatedEventArgs(pbNum, total, current, message);
                ProgressUpdated(args);
            }
        }
    }

    public class ProgressUpdatedEventArgs : EventArgs
    {
        public ProgressUpdatedEventArgs(int pbNum, int total, int progress, string message = "")
        {
            this.PBNum = pbNum;
            this.Total = total;
            this.Processed = progress;
            this.Message = message;
        }
        public string Message { get; private set; }
        public int PBNum { get; private set; }

        public int Processed { get; private set; }
        public int Total { get; private set; }
    }
}

【讨论】:

  • 我仍在努力思考委托部分,但它完全符合我的需要。
【解决方案2】:

嗯,你正在显示一个假的进度条......如果这是你想要的,那么这个通用方法是可以的。这里有两个改进:

  1. 不要使用 DoEvents。相反,只需保持 UI 的消息循环畅通。立即处理活动账单。
  2. 有些人可能更喜欢在适当的时间间隔内设置一个或三个计时器的解决方案。我认为这会更好,因为您不需要后台线程并且它们更精确。也就是说,我认为您的方法在这种情况下是可读的、可维护的并且通常很好。

【讨论】:

  • 感谢您的回复!我将如何实施这些项目?
  • 通过询问有关这些主题的特定问题或在网络上寻找大量教程。
  • 也许,但我还没有找到任何对我有帮助的东西。这是我最后的手段,我希望有人可以“打开灯”。
  • Google for "windows forms BackgroundWorker sample" 看看你是如何使用它的,所以你不需要使用 DoEvents。谷歌搜索“windows forms timer”,了解计时器的工作原理。
猜你喜欢
  • 1970-01-01
  • 2015-02-18
  • 2014-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多