Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forcing async-await IProgress<T>.Report() to be synchronous

I'm using Task-based Asynchronous Pattern (TAP) for some long task, using IProgress<T> to report progress to main UI. The Progress.Report seems to work only if it is preceded by another await task. If i use in an inline for loop, for example, reports messages are posted only at the end of the task:

public async Task<bool> DoSomething(IProgress<string> progress)
{
    progress.Report("Start");  // works
    await SomeTask();

    progress.Report("Message 1"); // works ONLY at end

    for ()
    {
        progress.Report("Message x"); // works ONLY at end
        // do some tasks inline
    }

    return true;
}

Is there some way to force Report messages to be posted synchronously? Thanks.

like image 226
ʞᴉɯ Avatar asked Oct 20 '25 14:10

ʞᴉɯ


1 Answers

The Progress.Report seems to work only if it is preceded by another await task.

That makes sense. Progress<T> captures the SynchronizationContext and posts to it once you invoke the Report method. If you're async method isn't really async and has mostly CPU work that is done on the UI thread, you're not freeing the message loop to process more events, hence you're only seeing it update at the end of the method call.

This is how Progress<T>.Report is implemented:

protected virtual void OnReport(T value)
{
        // If there's no handler, don't bother going through the [....] context.
        // Inside the callback, we'll need to check again, in case 
        // an event handler is removed between now and then.
        Action<T> handler = m_handler;
        EventHandler<T> changedEvent = ProgressChanged;
        if (handler != null || changedEvent != null)
        {
            // Post the processing to the [....] context.
            // (If T is a value type, it will get boxed here.)
            m_synchronizationContext.Post(m_invokeHandlers, value);
        }
}

In order to remain responsive, you can offload the for loop to a threadpool thread:

public async Task<bool> DoSomethingAsync(IProgress<string> progress)
{
    progress.Report("Start");  // works
    await SomeTask();
    progress.Report("Message 1");

    await Task.Run(() =>
    {
        progress.Report("Message x");
        // Do more CPU bound work
    }
    return true;
}
like image 190
Yuval Itzchakov Avatar answered Oct 23 '25 06:10

Yuval Itzchakov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!