问题
In my WPF program it took huge processing time and freezing for long time.
so I decided to use background worker and process it in background.
but it does not work. through debug, the program stop at Render3D()
. It does not throw exception. Its like when you put return
.
In other word it does nothing after reaching Render3D()
and will just return.
(I don't say it will return Because im not sure but the behavior is same as return)
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() works fine without Background processing but will freeze for some time.
Render3D() is in Partial
class of MainWindow
.because there are lots of methods so i decided to separate them.
Also how can I use ReportProgress
outside backgroundWorker1_DoWork
. for example in Render3D().
Last thing : i want to know how to show the user how much of process is done.
Solved!:
The problem was because i set Viewport3D
inside Render3D()
I separated it from Render3D and problem got fixed. thanks to Henk Holterman for the right answer.
It seems some tasks cant be done in another Thread. with the Error report i find out that the invalid task is setting Viewport3D properties.
this tasks must be done in Main thread.
below is invalid Code that made background worker stop functioning.
DefineCamera();
Viewport.Children.Add(model); // Must be run in Main thread.
And this Part.
private void DefineCamera()
{
PerspectiveCamera camera = new PerspectiveCamera
{
FieldOfView = 60
};
PositionCamera(camera);
Viewport.Camera = camera; // Must be run in Main thread.
}
回答1:
First of all, you had trouble finding the error.
... the program stop at Render3D(). It does not throw exception. Its like when you put return.
What actually happened was that an exception was thrown by your method and was captured by the Backgroundworker. It is transferred to the Completed event but you do have to act on it there.
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
}
Failing to look at either e.Error
or e.Result
is the same as having an empty catch{}
block in your program.
And with error handling in place we then have
oh yes it shown Error. System.InvalidOperationException the calling thread cannot access this object because a different thread owns it
This indicates that your Render3D() still interacts with the GUI somewhere.
The basic advice is to separate all the calculation (and I/O, database) work from the UI work. You can run the CPU bound and I/O bound cod in a thread but the GUI is single threaded, you can only interact with it from the main Thread.
回答2:
In the world of WPF, unlike Windows Forms that you were used to, you should consider Dispatcher. To do this, you have to import System.Windows.Threading
private void ThreadTask()
{
Thread.Sleep(TimeSpan.FromSeconds(5));
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart)delegate()
{
//Do some heavy task here...
});
}
Quick Update
In order to run the thread from a button click or whatever, any function, add this line of code:
Thread thread = new Thread(new ThreadStart(ThreadTask));
thread.Start();
This line of code is equivalent to BackgroundWorker.RunWorkerAsync();
回答3:
I would highly recommend using async/await. This feature was introduced in .NET 4.5, and is used to shift work off the main WPF GUI thread to make the application fast and responsive.
Essentially, the rule is to push any calculations which do not interact with GUI onto a background thread using a combination of Task.Run and async/await. Together with Dispatcher.Invoke, you don't really need anything else.
For example, a slow data call that might fetch data from the database could be pushed onto a background thread, so the application does freeze while it waits for the SQL to execute.
I've used this to make the applications that I write fast, responsive and snappy.
来源:https://stackoverflow.com/questions/30698429/background-worker-does-not-work-wpf