【问题标题】:BackgroundWorker updating GUI without ProgressChanged in WPF在 WPF 中没有 ProgressChanged 的​​ BackgroundWorker 更新 GUI
【发布时间】:2013-11-26 09:31:12
【问题描述】:

我有一个程序 (WPF),它每隔 10 秒执行一次测量。测量时间各不相同,因此可能需要 1 秒或 3 秒,例如:

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

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

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

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

现在我刚刚实现了使用“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 while 循环内)但现在如果我启动 measure_interval_worker,GUI 会冻结。

那我该如何解决呢?

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

【问题讨论】:

  • 在调试器中按“停止”并分析程序阻塞 GUI 的调用堆栈。整个程序是不是死锁了?
  • 在当前表格中,没有保证 measure_workers 是完整的。我同意其他 cmets 不要在 BackgroundWorker 中使用 BackgroundWorker。我会在里面使用 BlockingCollection。如果 CancellationPending 我会设置 e.Cancel = true;

标签: c# wpf backgroundworker


【解决方案1】:

activeDevices 很可能不是线程安全的。如果是List&lt;T&gt;,那肯定不是。例如,使用 ConcurrentBag。

此外,您不应从另一个后台工作人员的 DoWork 方法创建 BackgroundWorker。它不会按预期工作(因为它不是从 GUI 线程创建的)。

【讨论】:

  • 在backgroundworker中使用backgroundworker会出现什么问题(第二个backgroundworker与ui无关,它只是获取measuredata并将其存储到一个类中)
  • ProgressChangedRunWorkerCompleted 事件在创建BackgroundWorker 的线程中运行(如果该线程支持这一点,WPF-GUI 线程会这样做)。这就是BackgroundWorker 的实际用途,否则会令人困惑。尽管如此,这不应该是你的问题,但我觉得指出这一点会很有用。
  • 感谢您的任何建议,所以使用后台工作人员的原因是他们主要有ProgressChangedRunWorkerCompleted?这个事件是在创建工作者的线程中运行的(在大多数情况下是 ui 线程)?所以像Sheridan说的那样使用Task会更好吗?我还查看了 ConcurrentBag,尽管在使用该包的特殊情况下我看不到任何优势
  • 是 - 直接任务或线程池。 ConcurrentBag 是一个线程安全的集合。我猜(你省略了什么)每个线程都删除了“它的”设备 vom activeDevices。如果线程共享资源(activeDevices),它们必须同步访问。
  • 所以如果两个工人想同时从List&lt;T&gt; 中删除他们的“名字”,会发生错误吗?这是问题吗?
【解决方案2】:

BackgroundWorker 是在 .NET 中使用多线程的一种快速简便的方法,但它可能不是在您的方案中是最佳选择。任何时候你想要的东西比BackgroundWorker 类提供的更多,那么你需要考虑使用.NET 4.0 中的Task class,或者.NET 4.5 中新的asyncawait 关键字。

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

Start Multiple Async Tasks and Process Them As They Complete (C# and Visual Basic)
Asynchronous Programming with Async and Await (C# and Visual Basic)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多