Reputation: 23831
All, I have an expensive task which I undertake using the async
/await
keywords under .NET4.5. I report progress from the background thread via the Progress<T>
class.
The object I pass into IProgress<T>
as the type is ProgressInfo
which I create as a singleton class to avoid the overhead of creating and disposing of this type of object a great number of times during a run. The class is
public class ProgressInfo : IDisposable
{
public static ProgressInfo instance = null;
public WorkbookProgressInfo workbookinfo { get; set; }
public string progressMessage { get; set; }
public int progressPercentage { get; set; }
// Default constructor which has a few overloads
// for the class members above.
protected ProgressInfo()
{
}
...
// The default instance creator for the class.
// This also has overloads to allow changes to the
// class variables.
public static ProgressInfo Instance()
{
if (instance == null)
instance = new ProgressInfo();
return instance;
}
...
}
I report the progress via the method ReportProgress
and set up my IProgress<ProgressInfo>
as
IProgress<CostEngine.ProgressInfo> progressIndicator =
new Progress<CostEngine.ProgressInfo>(ReportProgress);
and reporting from the background thread is typically done using a global ProgressInfo progressInfo
and global IProgress<ProgressInfo> progressIndicator
like
...
progressInfo = new ProgressInfo.Instance(workbookinfo, message, n);
progressIndicator.Report(progressInfo);
...
The problem is that for runs that are small and execute quickly the ProgressInfo
object passed in to ReportProgress
changes as ReportProgress
is executing, so I test
if (progressInfo.workbookinfo != null)
{
// Do stuff <- here progressInfo.workbookinfo is changing to null!
}
How can I avoid this issue whilst keeping the expense of reporting the progress at a minimum?
Thanks for your time.
Upvotes: 1
Views: 1297
Reputation: 174457
You are trying to optimize the wrong part of your application here.
Several thousand objects are nothing for an application. Creating them most likely won't be a performance problem in your application. However, what could be a problem is the updating of the UI. If those thousands of progress reports are happening in a short period of time, you will update your UI constantly. Updating the UI costs time, so if you will run into a bottle neck, it will be here.
The correct approach here is to only report what you need to report.
For example, when you have 60.000 iterations that happen in - let's say - a minute and you report every single one of these iterations, you will try to update the UI every millisecond.
Do you really need 1000 FPS for your progress bar? I doubt it.
It would be perfectly good to only report every second. However, reporting every second creates it's own kind of overhead.
What I am doing in my applications is to report as soon as the percentage changed.
That would result in a maximum of 100 reports, no matter how many iterations you actually have.
In the above example I would report every 600 iterations.
To simplify this, I actually created a class called ProgressReporter
.
In its constructor, it takes a factory delegate that creates the ProgressInfo
object and the total number of iterations.
It provides a ReportProgress
method you pass in the current iteration.
The class internally stores the previously reported percentage and when calling this method it calculates the new percentage. Only if the differ will it use the factory delegate to create the new ProgressInfo
and call Report
on the IProgress<T>
instance.
BTW: If your background operation doesn't take minutes but hours, you can change this to not report when the percentage changed but when the first or second decimal of the percentage changed.
The point here is: Find an amount of reports that gives the user a feedback every few seconds.
Upvotes: 4