I would like to ask you on your opinion about the correct architecture when to use Task.Run
. I am experiencing laggy UI in our WPF .NET 4.5
application (with Caliburn Micro framework).
Basically I am doing (very simplified code snippets):
public class PageViewModel : IHandle<SomeMessage>
{
...
public async void Handle(SomeMessage message)
{
ShowLoadingAnimation();
// Makes UI very laggy, but still not dead
await this.contentLoader.LoadContentAsync();
HideLoadingAnimation();
}
}
public class ContentLoader
{
public async Task LoadContentAsync()
{
await DoCpuBoundWorkAsync();
await DoIoBoundWorkAsync();
await DoCpuBoundWorkAsync();
// I am not really sure what all I can consider as CPU bound as slowing down the UI
await DoSomeOtherWorkAsync();
}
}
From the articles/videos I read/saw, I know that await
async
is not necessarily running on a background thread and to start work in the background you need to wrap it with await Task.Run(async () => ... )
. Using async
await
does not block the UI, but still it is running on the UI thread, so it is making it laggy.
Where is the best place to put Task.Run?
Should I just
Wrap the outer call because this is less threading work for .NET
, or should I wrap only CPU-bound methods internally running with
Task.Run
as this makes it reusable for other places? I am not sure here if starting work on background threads deep in core is a good idea.
Ad (1), the first solution would be like this:
public async void Handle(SomeMessage message)
{
ShowLoadingAnimation();
await Task.Run(async () => await this.contentLoader.LoadContentAsync());
HideLoadingAnimation();
}
// Other methods do not use Task.Run as everything regardless
// if I/O or CPU bound would now run in the background.
Ad (2), the second solution would be like this:
public async Task DoCpuBoundWorkAsync()
{
await Task.Run(() => {
// Do lot of work here
});
}
public async Task DoSomeOtherWorkAsync(
{
// I am not sure how to handle this methods -
// probably need to test one by one, if it is slowing down UI
}
await Task.Run(async () => await this.contentLoader.LoadContentAsync());
should simply beawait Task.Run( () => this.contentLoader.LoadContentAsync() );
. AFAIK you don't gain anything by adding a secondawait
andasync
insideTask.Run
. And since you aren't passing parameters, that simplifies slightly more toawait Task.Run( this.contentLoader.LoadContentAsync );
. – ToolmakerSteveawait Task.Run(() => { RunAnySynchronousMethod(); return Task.CompletedTask; });
inside your async method (e.g. async controller method, test method etc). – Matt