在我的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()位于Partial
的{{1}}类中。因为有很多方法所以我决定将它们分开。
另外,如何在MainWindow
之外使用ReportProgress
。例如在Render3D()中。
最后一件事:我想知道如何向用户展示完成了多少过程。
解决:<!/强>
问题是因为我在backgroundWorker1_DoWork
Viewport3D
我将它与Render3D分开并解决了问题。感谢Henk Holterman的正确答案。
似乎有些任务无法在另一个Thread中完成。使用错误报告,我发现无效任务正在设置Viewport3D属性。
此任务必须在主线程中完成。
下面的是使后台工作程序停止运行的无效代码。
Render3D()
这部分。
DefineCamera();
Viewport.Children.Add(model); // Must be run in Main thread.
答案 0 :(得分:4)
首先,您无法找到错误。
......程序在Render3D()处停止。它不会抛出异常。就像你放回来一样。
实际发生的是您的方法抛出异常并被Backgroundworker捕获。它被转移到已完成的事件,但您必须在那里采取行动。
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.Error
或e.Result
与程序中的空catch{}
块相同。
如果错误处理到位,我们就有
哦是的,它显示错误。 System.InvalidOperationException调用线程无法访问此对象,因为另一个线程拥有它
这表明您的Render3D()仍在某处与GUI交互。
基本建议是将所有计算(和I / O,数据库)工作与UI工作分开。您可以在线程中运行CPU绑定和I / O绑定cod,但GUI是单线程的,您只能从主线程与它进行交互。
答案 1 :(得分:0)
在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();
答案 2 :(得分:-1)
我强烈建议使用async / await。此功能是在.NET 4.5中引入的,用于将工作从主WPF GUI线程转移到使应用程序快速响应。
基本上,规则是使用 Task.Run 和 async / await 的组合将任何不与GUI交互的计算推送到后台线程。与 Dispatcher.Invoke 一起,您实际上不需要任何其他内容。
例如,可能会从数据库中获取数据的慢速数据调用可以推送到后台线程,因此应用程序会在等待SQL执行时冻结。
我已经用它来制作我写得快速,反应迅速且活泼的应用程序。