BackgroundWorker在WPF中更新没有ProgressChanged的GUI

时间:2013-11-26 09:31:12

标签: c# wpf backgroundworker

我有一个程序(WPF),每次执行测量,比方说,10秒。测量时间会有所不同,因此可能需要1秒或3秒,例如:

|o........|..o......|o........|...o.....|o..

|...measurement start   o...measurement finished

我想要一个ProgressBar,它显示下一个测量任务何时开始。 此外,每次完成测量时,我都会将值添加到图形元素中以显示它们。

现在问题是:我有一个BackgroundWorker来检查它是否有时间进行测量。如果是时候,BackgroundWorker会启动执行测量的其他工作人员。

现在我刚刚实现了使用“bw.ReportProgress(0)”完成测量时显示数据的函数(0是一个没有用的虚拟值):

private void measure_interval_worker_DoWork(object sender, DoWorkEventArgs e)
{
        BackgroundWorker bw = sender as BackgroundWorker;
        DateTime timeToMeasure = DateTime.Now; 
        Boolean varsSaved = true; //if vars are saved

        while (!measure_interval_worker.CancellationPending)
        {                

            if (activeDevices.Count == 0 && varsSaved == false) //Devives are finished and vars aren't save yet
            {
                bw.ReportProgress(0); //In ProgressChanged the values are displayed
                varsSaved = true;
            }

            if (DateTime.Now >= timeToMeasure && activeDevices.Count == 0) //If its time to measure and all devices are done with measurement
            {
                timeToMeasure = DateTime.Now + measure_gap; //Next time to measure
                measure_workers = new List<BackgroundWorker>(); //Deleting the "old" workers?


                foreach (var dev in devices)
                {
                    //Add Key
                    varsSaved = false;
                    activeDevices.Add(dev.Key);

                    //And start worker
                    measure_workers.Add(new BackgroundWorker());
                    measure_workers[measure_workers.Count - 1].DoWork += measure_perform_DoWork;
                    measure_workers[measure_workers.Count - 1].RunWorkerAsync(dev.Key);
                }
            }
        }
    }

activeDevices只是一个列表,其中measure_workers删除了它们的名称,如果这样做,则activeDevices.Count == 0表示所有测量任务都已完成。

我现在尝试使用调度程序作为进度条,如:

this.Dispatcher.BeginInvoke((Action)delegate()
            {
                pbProgress.Value = (int)((timeToMeasure - DateTime.Now).TotalSeconds/ measure_gap.TotalSeconds * 100);
            });

(在循环时把它放在measure_interval_worker里面)但是现在如果我启动了measure_interval_worker,GUI会冻结。

那我怎么解决这个问题呢?

我可以使用ReportProgress进行ProgressBar但是我在显示数据方面遇到了同样的问题,因为我不知道值何时可以显示。

2 个答案:

答案 0 :(得分:1)

activeDevices很可能不是线程安全的。如果它是List<T>,那肯定不是。例如,使用ConcurrentBag。

此外,您不应该使用其他背景工作者的BackgroundWorker方法创建DoWork。它不会按预期工作(因为它不是从GUI线程创建的)。

答案 1 :(得分:0)

BackgroundWorker是一种在.NET中使用多线程的快捷方式,但它可能是您方案中的最佳候选者。任何时候你想要的东西超过BackgroundWorker类所提供的东西,那么你需要考虑使用.NET 4.0中的Task class或新的async和{{3 .NET 4.5中的关键字。

使用这些类,您将能够一起运行所有测量,并让UI异步更新其进度。您可以在MSDN的以下页面中找到如何使用它们:

await
Start Multiple Async Tasks and Process Them As They Complete (C# and Visual Basic)