I've been using Progress<T>
and wondered if it can be replaced by Action<T>
.
In the code below, using each of them for reporting progress, i.e. ReportWithProgress()
or ReportWithAction()
, didn't make any noticeable difference to me. How progressBar1
increased, how the strings were written on the output window, they seemed the same.
// WinForm application with progressBar1
private void HeavyIO()
{
Thread.Sleep(20); // assume heavy IO
}
private async Task ReportWithProgress()
{
IProgress<int> p = new Progress<int>(i => progressBar1.Value = i);
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Progress : " + i);
p.Report(i);
}
}
private async Task ReportWithAction()
{
var a = new Action<int>(i => progressBar1.Value = i);
for (int i = 0; i <= 100; i++)
{
await Task.Run(() => HeavyIO());
Console.WriteLine("Action : " + i);
a(i);
}
}
But Progress<T>
can't be a reinvention of the wheel. There should be a reason why it was implemented. Googling "c# Progress vs Action" didn't give me much help. How is Progress different from Action?
The difference is that with a
Progress<T>
you have an event where multiple listeners can listen for progress andProgress<T>
does capture theSynchonizationContext
when the instance is constructed and thus does not need to be invoked to the GUI-thread if created in the GUI-thread.You can also add multiple listeners to an
Action<T>
(thanks to @Servy for pointing that out), but each of them are then executed in the thread which invokes the action.Think of the following extended example, where the
Progress<T>
will work, but theAction<T>
will throw an exception:Calling
progressBar1.Value = i
from a different thread results in the dreaded "cross-thread operation not valid" exception. TheProgress
class, on the other hand, dispatches the event to the synchronization context captured in the moment of construction:This ensures that all updates to progress bars, labels and other UI elements are done on a (one and only) GUI thread.
So, it only makes sense to instantiate the
Progress
class outside of the background thread, inside a method which is called on a UI thread: