【问题标题】:WPF multi-thread progress updateWPF多线程进度更新
【发布时间】:2015-05-15 11:51:21
【问题描述】:

我需要向用户显示某种形式的反馈,而后台会发生一个小过程(7-10 秒)。

过去我在 Windows 窗体中使用单独的线程和 BackgroundWorker 没有任何问题,但在 WPF 中证明是困难的。

我已经阅读了很多关于这方面的文章,以及我应该如何在 WPF 中使用调度程序来启动一个新线程等。但是,当我尝试使用 BackgroundWorker 来显示一种等待图像反馈的形式时,它只是保持静止。

我不认为这很重要,但它使用来自 FirstFloor (https://github.com/firstfloorsoftware/mui) 的 mui。

我正在尝试使用内置的 ProgressRing 功能(在同一线程中运行时没有问题,并且没有其他主要任务在后台运行。

添加 BackgroundWorker,由于对象的跨线程访问而带来异常,尽管许多博客声明 WPF 中的 BackgroundWorks 具有跨线程感知能力并且可以安全运行。

以下是生成我需要的最接近的代码。

    private async void MyTaskProcess()
    {

        await Dispatcher.BeginInvoke(DispatcherPriority.Send, new ThreadStart(() =>
        {
            try
            {
                //Update the waiting ring image
                ProgressRing.IsActive = true;
            }
            catch
            {
                ProgressRing.IsActive = false;
                MessageBox.Show("Exception Thrown");
            }
        }));


        await Dispatcher.BeginInvoke(DispatcherPriority.Background, new ThreadStart(() =>
        {
            try
            {        
                //Run the main MS Excel export function
                Export2Excel();
                ProgressRing.IsActive = false;
            }
            catch
            {
                MessageBox.Show("Exception Thrown");
            }
        }));

    }

感谢任何反馈。

【问题讨论】:

  • 我认为您需要去阅读更多内容 - Dispatcher 不会启动新线程。相反,它确保代码在 UI 线程上执行!我建议使用Task<T>async/await 查看TPL。 BackgroundWorker 有点老了,但仍然可以正常工作。
  • 查看 MSDN 上的 BackgroundWorker Class 页面。
  • 在使用后台工作线程时,您是否尝试过将 WorkerReportsProgress 设置为 true,并调用 ReportProgress?引发 ProgressChanged 事件时,它会在创建后台工作线程的线程上执行,因此更新 UI 应该没有问题。

标签: c# wpf multithreading


【解决方案1】:

在现代 WPF 应用程序中执行此操作的方式是启动一个新的 Task,您可以在其中完成工作;在幕后,这将在线程池线程上执行工作:

Task.Factory.StartNew(this.DoWork)

现在在DoWork 报告进度,您InvokeAsync 会在端口计数发生变化时返回主线程:

void DoWork()
{
    foreach(var item in this.WorkItems)
    {
        // Do something

        // Report Progress
        ++progress
        Application.Current.Dispatcher.InvokeAsync(() => this.Progress = progress);
    }
}

【讨论】:

  • 谢谢布赖恩,似乎是一个更好的选择。我会调查的。
  • 没有问题;正如斯蒂芬所说,可能有更多奇特的方式来报告进度,但基本上你需要做的就是将你的进度值返回到主线程,一切都使用调度程序在幕后完成。
【解决方案2】:

添加 BackgroundWorker,由于对象的跨线程访问而带来异常,尽管许多博客声明 WPF 中的 BackgroundWorks 具有跨线程感知能力并且可以安全运行。

BackgroundWorker 可以很好地与 WPF 配合使用,只要您在 UI 线程上创建并启动 BGW。 (附带说明,BGW 对 Windows 窗体有相同的限制)。正如其他评论者所指出的,使用 BGW 进行进度更新的正确方法是使用 ReportProgress,而不是 Dispatcher

但是,我建议使用较新的 Task.RunIProgress<T> 来进行进度更新。我有一个blog post,它将旧的 BGW 进度更新与基于 IProgress<T> 的新进度更新进行比较/对比。

很难说您的代码应该是什么样子,因为您发布的代码实际上并没有在后台线程上运行任何东西。特别是,如果 Export2Excel 必须在 UI 线程上运行,而这就是您所做的所有工作,那么使用 BGW 或 Task.Run 根本没有意义,因为无论如何都无法在后台线程上运行。

【讨论】:

  • 我试图通过BackgroundWorker的其他功能跨主UI线程访问WPF对象,一旦我意识到,我只通过ProgressChanged访问主UI线程,这似乎是一个安全的交叉线程方法,一切都很好。
【解决方案3】:

你去,到我写的下面的链接仔细阅读。希望你能彻底解决你的问题:

Threads in WPF

【讨论】:

  • 我试图访问构成主 UI 线程一部分的对象,因此在我的代码中出现异常。在阅读了您的帖子(非常详细且介绍得很好)后,我明白了,我需要在 DoWork 中运行所有后台处理,并通过 ProgressChanged 简单地更新用户。再次感谢。
猜你喜欢
  • 2021-07-16
  • 2011-08-12
  • 2010-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-22
  • 2015-07-30
  • 1970-01-01
相关资源
最近更新 更多