【问题标题】:c# BackgroundWorker DoWork method calling another class and ProgressReportc# BackgroundWorker DoWork 方法调用另一个类和 ProgressReport
【发布时间】:2012-08-13 14:53:17
【问题描述】:

我正在运行一个 BackgroundWorker 线程来执行一项耗时的任务。耗时的任务在另一个班级。我需要将在 BackgroundWorker 上运行的这个单独的类上取得的进展传回 Main Form1 类。我不知道如何解决这个问题。请提供建议。先感谢您。

    **// Main Form1 UI Class**    

    public void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
    {
        //e.Argument always contains whatever was sent to the background worker
        // in RunWorkerAsync. We can simply cast it to its original type.
        DataSet ds = e.Argument as DataSet;
        this.createje.ProcessData(this.ds);
    }

    private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.progressBar1.Minimum = 0;
        this.progressBar1.Maximum = CreateJE.max;
        this.progressBar1.Value = e.Recno;
    }

    **//Other Class called CreateJE**

    public void ProcessData(DataSet ds)
    {
        //Do time consuming task...
     for (int i = 1; i <= 10; i++)
        {
            if (worker.CancellationPending == true)
            {
                e.Cancel = true;
                break;
            }
            else
            {
                // Perform a time consuming operation and report progress.
                System.Threading.Thread.Sleep(500);

                **//How do I report progress back to the Main UI?** 
                //worker.ReportProgress(i * 10);
            }
        }
    }

【问题讨论】:

    标签: c# backgroundworker


    【解决方案1】:

    最简洁、最可扩展的解决方案可能是让您的ProcessData() 方法引发BackgroundWorker 正在侦听的事件。这样ProcessData() 不依赖于BackgroundWorker 作为调用者。 (您还需要设法取消ProcessData())。如果需要,您甚至可以重复使用 ProgressChangedEventArgs。例如(未测试,但你明白了吗?):

    partial class Form1 { 
        public void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e) {
            //e.Argument always contains whatever was sent to the background worker 
            // in RunWorkerAsync. We can simply cast it to its original type.
            DataSet ds = (DataSet)e.Argument;
            var bgw = (BackgroundWorker)sender;
    
            var eh = new ProgressChangedEventHandler((o,a) => bgw.ReportProgress(a.ProgressPercentage));
            createje.ProgressChanged += eh;
            this.createje.ProcessData(this.ds));
            createje.ProgressChanged -= eh; //necessary to stop listening
        }
    
        private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.progressBar1.Minimum = 0;
            this.progressBar1.Maximum = CreateJE.max;
            this.progressBar1.Value = e.ProgressPercentage;
        }
    
    }
    
    partial class CreateJE { 
    
        public event ProgressChangedEventHandler ProgressChanged; 
        protected virtual void OnProgressChanged(ProgressChangedEventArgs e)
        { 
            var hand = ProgressChanged; 
            if(hand != null) hand(this, e);
        }
    
        public void ProcessData(DataSet ds)
        {
            for(int i = 1; i <= 10; i++)
            {
                // Perform a time consuming operation and report progress.
                System.Threading.Thread.Sleep(500);
    
                var e = new ProgressChangedEventArgs(i * 10, null);
            }
        }
    
    }
    

    快速而肮脏的方法是将BackgroundWorker 作为参数传递给ProcessData()。恕我直言,这是相当丑陋的,但是,将您限制为仅使用 BackgroundWorkers,并且还迫使您在一个地方(主窗体类)定义 BackgroundWorker 并在另一个地方(CreateJE 类)定义 ReportProgress 的返回值。

    您还可以使用计时器并每隔 X 毫秒报告一次进度,查询 CreateJE 对象的进度。这似乎与您的其余代码一致。这样做的问题是它会使您的 CreateJE 类对多线程不友好。

    【讨论】:

    • 我喜欢你的第一个建议。你能提供一个代码示例吗?
    【解决方案2】:

    最快和最简单的选择是在类CreateJE 中声明一个delegate,它将报告进度,然后将其挂钩到BackgroundWorkerReportProgress 方法。

    class CreateJE
    {
        public Action<int> ReportProgressDelegate{get;set;}
    
        public void ProcessData(DataSet ds)
        {
            for(int i = 0; i < 10; i++)
            {
                Thread.Sleep(500);
                ReportProgress(i*10);
            }
        }
    
        private void ReportProgress(int percent)
        {
            if(ReportProgressDelegate != null)
                ReportProgressDelegate(percent);
        }
    }
    

    在您的表单中,初始化您的实例的ReportProgressDelegate 属性(我假设this.createje 指的是表单的一个字段,所以OnLoad 似乎是进行初始化的好地方):

    protected override void OnLoad(EventArgs e)
    {
        this.creatje.ReportProgressDelegate = worker.ReportProgress;
    }
    

    之后,您可以使用已有的事件处理程序(backgroundWorker2_DoWorkbackgroundWorker2_DoWork)。

    PS:您应该对worker.CancellationPending 属性使用相同的方法。

    【讨论】:

      猜你喜欢
      • 2015-02-01
      • 2021-01-03
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      • 2020-10-20
      • 2014-01-31
      • 2016-01-10
      • 1970-01-01
      相关资源
      最近更新 更多