Reputation: 571
I have a single-threaded program which executes complex, time consuming calculations and reports the progress of these.
private void BtnExecute_Click()
{
ComplexCalculation(Object1);
ComplexCalculation(Object2);
ComplexCalculation(Object3);
}
The ComplexCalculation method looks like this:
private void ComplexCalculation(Object MyObject)
{
(...do some time consuming operation on MyObject...);
WriteToTextboxGUI("50% Complete" + MyObject.Name);
(...do another time consuming operation on MyObject...);
WriteToTextboxGUI("100% Complete" + MyObject.Name);
}
In the above code, the WriteToTextboxGUI(string)
method updates a textbox on the GUI with the progress.
I'm looking to adopt a multi-threaded approach so that the GUI remains responsive while the complex calculations are carried out. I have read up quite a bit about BackgroundWorker
and Threads
in general, and then how this was simplified/improved when Task
and TPL came onto the scene with .Net 4.0, and now how Async
and Await
have arrived with .Net 4.5, and would like to know if these more recent technologies can allow me to re-write my application (in relatively simple code) so that it can:
Can anyone can point me in the direction of a simple solution which would satisfy these three criteria?
P.S. This application will not be running on a server, it will be running in WPF on the desktop.
Upvotes: 1
Views: 1116
Reputation: 457057
I recommend that you use IProgress<T>
for progress reports and Task.Run
to start background tasks. I recently finished a blog series showing that Task.Run
is superior to BackgroundWorker
.
In your case:
private sealed class ComplexCalculationProgress
{
public string Name { get; set; }
public int PercentComplete { get; set; }
}
private void ComplexCalculation(Object MyObject, IProgress<ComplexCalculationProgress> progress)
{
(...do some time consuming operation on MyObject...);
if (progress != null)
progress.Report(new ComplexCalculationProgress { Name = MyObject.Name, PercentComplete = 50 });
(...do another time consuming operation on MyObject...);
if (progress != null)
progress.Report(new ComplexCalculationProgress { Name = MyObject.Name, PercentComplete = 100 });
}
Note that since your background operation no longer calls WriteToTextboxGUI
, it has better separation of concerns. You'll find that the design of IProgress<T>
encourages a better design in your own code.
You can call it (using simple parallelism) as such:
private async void BtnExecute_Click()
{
var progress = new Progress<ComplexCalculationProgress>(update =>
{
WriteToTextboxGUI(update.PercentComplete + "% Complete " + update.Name);
});
await Task.WhenAll(
Task.Run(() => ComplexCalculation(Object1, progress)),
Task.Run(() => ComplexCalculation(Object2, progress)),
Task.Run(() => ComplexCalculation(Object3, progress))
);
}
Alternatively, you can easily use "real" parallelism:
private async void BtnExecute_Click()
{
var progress = new Progress<ComplexCalculationProgress>(update =>
{
WriteToTextboxGUI(update.PercentComplete + "% Complete " + update.Name);
});
var objects = new[] { Object1, Object2, Object3 };
await Task.Run(() =>
Parallel.ForEach(objects, o => ComplexCalculation(o, progress))
);
}
Upvotes: 7
Reputation: 1808
Background workers can do all this easily.
Use multiple threads simultaneously to execute complex calculations
Use a new background worker for each calculation.
Report progress to my GUI as before
Use WorkerReportsProgress=True, with the ReportProgress() method and ProgressChanged event.
Maintain a responsive GUI throughout
Yep.
Upvotes: 0