【问题标题】:C# BackgroundWorker and ProgressBar issueC# BackgroundWorker 和 ProgressBar 问题
【发布时间】:2017-02-09 06:07:40
【问题描述】:

我有两个文件。一个包含 DataProgressBar 类型的变量,并在加载自己的组件之前调用 startAsyncWorker() 方法。另一个是下面显示的 DataProgressBar。当我运行我的程序并在其他文件中调用 startAsyncWorker() 时,行为

预期: 显示带有进度条的小窗口,在执行 WorkerDatabaseInsertion 中的工作时从 0 加载到 100。然后,当完成包含 DataProgressBar 的第一个类文件时,将继续执行下一条指令。

经验丰富:显示带有进度条的小窗口,没有对 UI 进行任何更改,线程似乎冻结,因为没有显示输出或处理证据。我关闭窗口,调用文件继续。

public partial class DataProgressBar : Window
    {
        private BackgroundWorker bgw;
        private String _path;
        private LFPReader _lfp;
        private Access db;


            public DataProgressBar(String p, LFPReader reader, Access database)
            {
                InitializeComponent();
                /* Set private class level variables */
                _path = p;
                _lfp = reader;
                db = database;
                db.open();
                /* Set up worker and progressbar */
                bgw = new BackgroundWorker();
                SetUpWorker(bgw);
                progressbar.Maximum = 100;
                progressbar.Minimum = 0;
                progressbar.Value = 0;
            }

            public void startAsyncWorker()
            {
                if(bgw.IsBusy != true)
                {
                    bgw.RunWorkerAsync();
                }
            }

            /// <summary>
            /// This methods exists for completeness, but we will
            /// probably not need to directly cancel the worker from here.
            /// --Kurtpr
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            public void cancelAsyncWorker(object sender, EventArgs e)
            {
                if (bgw.WorkerSupportsCancellation == true)
                {
                    bgw.CancelAsync();
                }
            }

            private void SetUpWorker(BackgroundWorker worker)
            {
                worker.WorkerReportsProgress = true; // we need this in order to update the UI
                worker.WorkerSupportsCancellation = true; // Not sure why, but we may need this to cancel

                worker.DoWork += new DoWorkEventHandler(WorkerDatabaseInsertion);
                worker.ProgressChanged += new ProgressChangedEventHandler(WorkerProgressChanged);
                worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(WorkerBurnNotice);
            }

            private void WorkerDatabaseInsertion(object sender, DoWorkEventArgs e) {
                BackgroundWorker worker = sender as BackgroundWorker;
                ImageInfo ImageData = new ImageDB.ImageInfo();
                double size = _lfp.GetImageData().ToArray().Length;
                int index = 0;
                //_path is setup in loadAsLFP() before this call.
                foreach (var image in _lfp.GetImageData())
                {
                    index++;
                    if (worker.CancellationPending == true)
                    {
                        e.Cancel = true;
                        break;
                    }
                    image.FullPath = Path.Combine(_path, image.FullPath);
                    ImageData.Add(new ImageDB(image));
                    db.insertImage(new ImageDB(image));
                    worker.ReportProgress((int)(index/size));
                }
            }

            private void WorkerProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                Console.WriteLine("Hello");
                progressbar.Value = e.ProgressPercentage;
            }

            private void WorkerBurnNotice(object sender, RunWorkerCompletedEventArgs e)
            {
                db.close();
            }
        }

我怀疑我对 BackgroundWorker 的一个基本方面是错误的。我的逻辑错误在哪里?

编辑这是调用和创建 DataProgressBar 对象的代码。

private void createDb(string filePath, LFPReader lfp)
        {
            //Set up LFP related objects.
            ImageData = new ImageDB.ImageInfo();

            //create database file.
            filePath = filePath.Replace(".lfp", ".mdb").Replace(".LFP", ".mdb");
            _lfpName = filePath; // update to use the database file
            Access db = new Access(filePath.Replace(".lfp", ".mdb"));
            db.createDatabase();

            //used for calculating progress of creating this new database.
            var progressbar = new DataProgressBar(_path, lfp, db);
            progressbar.ShowDialog();
            progressbar.startAsyncWorker();

            CurImage = ImageData.First().FullPath;
            //Make sure that data context is set for these images.
            DataContext = ImageData;

        }

【问题讨论】:

  • 我认为worker.ReportProgress((int)(index/size)); 应该是worker.ReportProgress((int)(index * 100/size));
  • 您的进度计算很可能已关闭。如果 index 从 0 变为 size 直到最后你只会得到零。虽然这不会冻结任何东西。什么是“调用文件”?你的意思是不同的班级?那是什么定义?
  • 你可以试试worker.ReportProgress((double)(index/size)); 我曾经有过类似的经历。即使这不起作用,也可以设置断点并继续执行。

标签: c# multithreading backgroundworker


【解决方案1】:

我认为你的进度计算逻辑在这里有问题。

worker.ReportProgress((int)(index/size));

此行将始终报告进度为 0。因此,您的进度条将始终卡在 0 位置。

相反,使用以下以百分比报告进度。

worker.ReportProgress((int)(index*100/size));

更新: 您共享的代码似乎是正确的。 我认为问题在于您实现进度条的方式。

我想你是从主线程调用 startAsyncWorker 方法如下;

var dpb = new DataProgressBar(p, reader, database);
dpb.startAsyncWorker();

以上两行调用后,你的主线程应该是空闲的。

这意味着,以下代码将导致您的进度条冻结 50 秒,因为即使您的 DoWork 运行良好,UI 也不会更新,因为主线程不是空闲的。

var dpb = new DataProgressBar(p, reader, database);
dpb.startAsyncWorker();
Thread.Sleep(50000);               //Main thread is busy for 50 seconds

更新 2:

真正的问题在于以下几行;

var progressbar = new DataProgressBar(_path, lfp, db);
progressbar.ShowDialog();
progressbar.startAsyncWorker();

实际上ShowDialog() 方法将DataProgressBar 显示为模态对话框。这意味着,除非您关闭该对话框,否则控件将不会转到下一行。

您的问题应该使用以下代码解决;

var progressbar = new DataProgressBar(_path, lfp, db);
progressbar.startAsyncWorker();
progressbar.ShowDialog();

它将首先启动您的后台工作程序,然后将显示 DataProgressBar 对话框。

【讨论】:

  • 感谢您的更正,但经过测试,进度条仍然没有响应。正如我所提到的,我认为这是 BackgroundWorker 的问题,因为附加到 worker.DoWork 的方法似乎永远不会被调用。
  • 这正是我在主线程中调用它的方式。那么在使用之前将后台工作方法和对象移动到主线程中吗?
  • 您不必从 BackgroundWorker 移动任何代码。只需确保调用dpb.startAsyncWorker(); 后主线程上没有执行任何操作。如果您在执行此操作时遇到任何问题,您应该共享调用 startAsyncWorker() 的代码。
  • 主线程是另一个 UI。这会引起问题吗?执行此操作后,它不会执行任何其他操作,因为必须先完成此过程才能显示任何内容。
  • @KurtPrice,请发布调用startAsyncWorker() 的方法以及创建DataProgressBar 的对象的代码。
猜你喜欢
  • 2010-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多