Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Task.Run and UI Progress Updates

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.";
}
like image 599
Alex Strickland Avatar asked Feb 10 '15 11:02

Alex Strickland


People also ask

What is IProgress in C#?

IProgress<T> InterfaceDefines a provider for progress updates.

Does Task run need to be awaited?

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.

Is Task run good?

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.

What is the use of task run in C#?

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.


3 Answers

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.

like image 119
Gusdor Avatar answered Oct 06 '22 08:10

Gusdor


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.

like image 28
Jean Hominal Avatar answered Oct 06 '22 08:10

Jean Hominal


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).

like image 8
Yury Schkatula Avatar answered Oct 06 '22 09:10

Yury Schkatula