【问题标题】:Background worker does not Work WPF后台工作人员不工作 WPF
【发布时间】:2015-06-07 21:02:12
【问题描述】:

在我的 WPF 程序中,它需要大量的处理时间并长时间冻结。

所以我决定使用后台工作者并在后台处理它。

但它不起作用。通过调试,程序停在Render3D()。它不会抛出异常。就像你输入return一样。

换句话说,它在到达Render3D() 后什么都不做,只会返回。

(我不是说它会返回,因为我不确定但行为与返回相同)

    private readonly BackgroundWorker backgroundWorker = new BackgroundWorker();
    private AssetDeclaration _assetDeclaration = new AssetDeclaration();

    public MainWindow()
    {
        backgroundWorker.DoWork += backgroundWorker1_DoWork;
        backgroundWorker.ProgressChanged += backgroundWorker1_ProgressChanged;
        backgroundWorker.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
        InitializeComponent();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i = 1; i <= 1000; i++)
        {
            if (!((BackgroundWorker)sender).CancellationPending)
            {
                Render3D(); // will return at this point. (why?) or waiting for something to start?
                ((BackgroundWorker)sender).ReportProgress(i);
            }
            else
            {
                e.Cancel = true;
                break;
            }
        }
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        MessageBox.Show("Done!");//will show message box instant.
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        ProgressBar1.Value = e.ProgressPercentage;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        //...Some work here before starting Hard job!
        //...From now i want to start heavy process in background.
        //...with report to progress bar at same time.
        backgroundWorker.RunWorkerAsync(100);
    }

Render3D() 在没有后台处理的情况下可以正常工作,但会冻结一段时间。

Render3D() 属于PartialMainWindow 类。因为有很多方法,所以我决定将它们分开。

另外,我如何在 backgroundWorker1_DoWork 之外使用 ReportProgress 。例如在 Render3D() 中。

最后一件事:我想知道如何向用户展示完成了多少流程。

已解决!

问题是因为我在Render3D() 中设置了Viewport3D

我将它从 Render3D 中分离出来,问题得到了解决。感谢 Henk Holterman 的正确答案。

似乎有些任务无法在另一个线程中完成。通过错误报告,我发现无效任务正在设置 Viewport3D 属性。

此任务必须在主线程中完成。

以下是导致后台工作人员停止运行的无效代码。

DefineCamera();
Viewport.Children.Add(model); // Must be run in Main thread.

还有这部分。

    private void DefineCamera()
    {
        PerspectiveCamera camera = new PerspectiveCamera
        {
            FieldOfView = 60
        };

        PositionCamera(camera);
        Viewport.Camera = camera; // Must be run in Main thread.
    }

【问题讨论】:

  • 您没有 Completed 事件。添加它并处理传入的e.Error。或者只是向 DoWork 添加一个 try/catch。您现在忽略了可以告诉您出了什么问题的异常。
  • "在我添加 RunWorkerCompleted 之后" 好的,现在让它报告e.Error
  • 好的,Render3D() 不适合在线程上运行。运动结束。
  • 我们不能评论/分析我们看不到的代码。 ViewPort 使用起来不安全。也许你可以拆分 GUI / 非 GUI 代码,我不知道。
  • +1 谢谢。生病尝试拆分非 GUI 部分。绘图模型只需要几毫秒。所以这可能会解决问题@HenkHolterman

标签: c# wpf backgroundworker


【解决方案1】:

首先,您很难找到错误。

...程序在 Render3D() 处停止。它不会抛出异常。就像你放回车一样。

实际发生的情况是您的方法抛出了异常,并被 Backgroundworker 捕获。它被转移到 Completed 事件,但您必须在那里对其采取行动。

private void worker_Completed(object sender, RunWorkerCompletedEventArgs e) 
{
  // check error, check cancel, then use result
  if (e.Error != null)
  {
     // handle the error
  }
  else if (e.Cancelled)
  {
     // handle cancellation
  }
  else      
  {         
      // use the result(s) on the UI thread
  }    
  // general cleanup
}

未能查看e.Errore.Result 与您的程序中有一个空的catch{} 块相同。

有了错误处理,我们就有了

哦,是的,它显示错误。 System.InvalidOperationException 调用线程无法访问此对象,因为不同的线程拥有它

这表明您的 Render3D() 仍在某处与 GUI 交互。

基本建议是将所有计算(以及 I/O、数据库)工作与 UI 工作分开。您可以在线程中运行 CPU 绑定和 I/O 绑定代码,但 GUI 是单线程的,您只能从主线程与其交互。

【讨论】:

    【解决方案2】:

    在 WPF 世界中,与您习惯的 Windows 窗体不同,您应该考虑 Dispatcher。为此,您必须导入System.Windows.Threading

        private void ThreadTask()
        {
            Thread.Sleep(TimeSpan.FromSeconds(5));
    
            this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                        (ThreadStart)delegate()
                        {
                            //Do some heavy task here...
                        });
        }
    

    快速更新

    为了通过按钮单击或其他任何函数运行线程,请添加以下代码行:

        Thread thread = new Thread(new ThreadStart(ThreadTask));
        thread.Start();
    

    这行代码相当于BackgroundWorker.RunWorkerAsync();

    【讨论】:

    • 有人能解释为什么投反对票吗?我以前用过没有任何问题!
    • 这毫无意义。即使我们忽略 Sleep(),“繁重的任务”也会被发送回 Gui 主线程。一点收获都没有。只是一个令人困惑的弯路。
    • 它可以工作,但不能解决您的冻结问题。
    • @HenkHolterman 我和调度员一起生活了将近两年,没有任何问题。我已经将它用于数据库应用程序开发(这是需要繁重任务的东西)。
    • @user3104111:就像 Henk 所说,您完全忽略了 OP 问题的根源。这与是否使用 BackgroundWorker 无关。如果我们要讨论最佳实践,那么使用 Tasks 和 async 将优于您的建议,更不用说您正在创建单个线程而不是使用标准线程池,我可以继续...... .
    【解决方案3】:

    我强烈推荐使用 async/await。此功能是在 .NET 4.5 中引入的,用于将工作从 WPF GUI 主线程转移,以使应用程序快速响应。

    本质上,规则是使用 Task.Runasync/await 的组合将任何不与 GUI 交互的计算推送到后台线程。与 Dispatcher.Invoke 一起使用,您真的不需要其他任何东西了。

    例如,可能会从数据库中获取数据的慢速数据调用可能会被推送到后台线程,因此应用程序在等待 SQL 执行时会冻结。

    我用它来使我编写的应用程序快速、响应迅速且快速。

    【讨论】:

    • 这些都不能解决 OP 面临的问题。
    • @HenkHolterman 我建议使用 async/await 进行重构。这将解决 OP 遇到的问题。
    • 不,它将遭受同样的异常。这里没有魔杖。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-09
    • 1970-01-01
    • 2014-04-14
    相关资源
    最近更新 更多