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.";
}
IProgress<T> InterfaceDefines a provider for progress updates.
If it is some trivial operation that executes quickly, then you can just call it synchronously, without the need for await . But if it is a long-running operation, you may need to find a way to make it asynchronous.
Run is completely unnecessary. If you do still need Task. Run , then it doesn't make much difference to the runtime in this case whether you call it once or many times, so just do what's most natural for your code.
The Run method allows you to create and execute a task in a single method call and is a simpler alternative to the StartNew method. It creates a task with the following default values: Its cancellation token is CancellationToken.
Progress<T>
catches the current SynchronisationContext
when it is instantiated. Whenever you call Report
, it secretly delegates that to the captured context. In the example, the captured context is the UI, meaning that no exceptions occur.
The Progress<T>
constructor captures the current SynchronizationContext
object.
The SynchronizationContext
class is a facility that abstracts the particulars of the involved threading model. That is, in Windows Forms it will use Control.Invoke
, in WPF it will use Dispatcher.Invoke
, etc.
When the progress.Report
object is called, the Progress
object itself knows that it should run its delegate using the captured SynchronizationContext
.
In other terms, it works because Progress
has been designed to handle that without the developer having to explicitly say it.
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": https://devblogs.microsoft.com/dotnet/async-in-4-5-enabling-progress-and-cancellation-in-async-apis
We introduced the IProgress interface to enable you to create an experience for displaying progress. This interface exposes a Report(T) method, which the async task calls to report progress. You expose this interface in the signature of the async method, and the caller must provide an object that implements this interface. Together, the task and the caller create a very useful linkage (and could be running on different threads).
We also provided the Progress class, which is an implementation of IProgress. You are encouraged to use Progress in your implementation, because it handles all the bookkeeping around saving and restoring the synchronization context. Progress exposes both an event and an Action callback, which are called when the task reports progress. This pattern enables you to write code that simply reacts to progress changes as they occur. Together, IProgress and Progress provide an easy way to pass progress information from a background task to the UI thread.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With