Search code examples
c#asynchronousasync-awaitmauiprogress

How can I get a progress report on a async function with many tasks, in which I am using await Task.WhenAll(tasks);


I already read all those posts:

Reporting progress from Async Task

https://devblogs.microsoft.com/dotnet/async-in-4-5-enabling-progress-and-cancellation-in-async-apis/

https://blog.stephencleary.com/2012/02/reporting-progress-from-async-tasks.html

But still I don't know how to report a Progress from many Tasks when I'm using await Task.WhenAll(tasks);

So, I am using this code:

public string StatusMessage { get; set; }
public async void ButtonClickHandler(object sender, EventArgs e)
{
    var tasks = new List<Task>
    {
        Task1,
        Task2,
        Task3,
        Task4,
        Task5
    };
    await Task.WhenAll(tasks);
}

So I want to get a change in StatusMessage when any on Task1..Task5 is completed. Somehow each task has to have a name and that name to be reported to StatusMessage variable.

I want also an event to rise when StatusMessage changes.

StatusMessage could be also a cumulative list or barely a string.

JSteward post from here it is interesting but I don't know how to convert my Task1..Task5 tasks in a WorkItem from his sample.

How can I do that?


Solution

  • You could define an extension method on the Task type, that reports progress on an IProgress<T> when the task completes successfully:

    public static async Task OnSuccessfulCompletion<T>(
        this Task task, IProgress<T> progress, T progressValue)
    {
        ArgumentNullException.ThrowIfNull(task);
        ArgumentNullException.ThrowIfNull(progress);
    
        await task.ConfigureAwait(false);
        progress.Report(progressValue);
    }
    

    And use it like this:

    public async void ButtonClickHandler(object sender, EventArgs e)
    {
        /* Launching the tasks Task1, Task2 etc is omitted */
        Progress<string> progress = new(value => textBox1.AppendText($"{value}\r\n"));
        List<Task> tasks = new()
        {
            Task1.OnSuccessfulCompletion(progress, "Task1"),
            Task2.OnSuccessfulCompletion(progress, "Task2"),
            Task3.OnSuccessfulCompletion(progress, "Task3"),
            Task4.OnSuccessfulCompletion(progress, "Task4"),
            Task5.OnSuccessfulCompletion(progress, "Task5"),
        };
        await Task.WhenAll(tasks);
    }
    

    Be aware that the Progress<T> type reports progress asynchronously. This means that the handler might be invoked after the completion of the ButtonClickHandler method. If you write anything directly on the textBox1, immediately after the line await Task.WhenAll(tasks);, this text might not be the last to be appended to the textBox1. In case you don't like this behavior, you can find a synchronous IProgress<T> implementation here.