Reputation: 5594
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.
Upvotes: 4
Views: 1965
Reputation: 149618
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;
}
Upvotes: 7