This code snippet is from Stephen Cleary's blog and gives an example of how to report progress when using Task.Run. I would like to know why there are no cross thread issues with updating the UI, by which I mean why is invoke not required?
private async void button2_Click(object sender, EventArgs e)
{
var progressHandler = new Progress<string>(value =>
{
label2.Text = value;
});
var progress = progressHandler as IProgress<string>;
await Task.Run(() =>
{
for (int i = 0; i != 100; ++i)
{
if (progress != null)
progress.Report("Stage " + i);
Thread.Sleep(100);
}
});
label2.Text = "Completed.";
}
Progress<T>
catches the currentSynchronisationContext
when it is instantiated. Whenever you callReport
, it secretly delegates that to the captured context. In the example, the captured context is the UI, meaning that no exceptions occur.It seems you're confused due the fact that part of this cross-thread machinery is hidden from developer eyes so you just have to "take and use": http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx
Just one more thing to mention: progress notification will be invoked after the part of the job is done, not just at that moment. So, if your UI thread is idling and you have spare CPU core the delay will be almost zero. If your UI thread is busy, the notification will not be invoked until the moment the UI thread is back to idle (regardless how much spare CPU cores your computer has).
The
Progress<T>
constructor captures the currentSynchronizationContext
object.The
SynchronizationContext
class is a facility that abstracts the particulars of the involved threading model. That is, in Windows Forms it will useControl.Invoke
, in WPF it will useDispatcher.Invoke
, etc.When the
progress.Report
object is called, theProgress
object itself knows that it should run its delegate using the capturedSynchronizationContext
.In other terms, it works because
Progress
has been designed to handle that without the developer having to explicitly say it.