【发布时间】:2022-01-07 02:41:54
【问题描述】:
我想询问您对何时使用Task.Run 的正确架构的看法。我在我们的 WPF .NET 4.5 中遇到了滞后的 UI
应用程序(使用 Caliburn Micro 框架)。
基本上我在做(非常简化的代码sn-ps):
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();
}
}
从我阅读/看到的文章/视频中,我知道await async 不一定在后台线程上运行,要在后台开始工作,您需要用 await Task.Run(async () => ... ) 包装它。使用 async await 不会阻塞 UI,但它仍然在 UI 线程上运行,因此会导致延迟。
Task.Run 的最佳放置位置在哪里?
我应该只是
包装外部调用,因为这对于 .NET 的线程工作较少
,或者我应该只包装在内部使用
Task.Run运行的受 CPU 限制的方法,因为这使它可以在其他地方重用?我不确定在核心深处的后台线程上开始工作是否是一个好主意。
广告(1),第一个解决方案是这样的:
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.
广告(2),第二种解决方案是这样的:
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
}
【问题讨论】:
-
顺便说一句,(1)
await Task.Run(async () => await this.contentLoader.LoadContentAsync());中的行应该只是await Task.Run( () => this.contentLoader.LoadContentAsync() );。 AFAIK 在Task.Run中添加第二个await和async不会获得任何收益。而且由于您没有传递参数,因此简化为await Task.Run( this.contentLoader.LoadContentAsync );。 -
如果你有第二个 await 里面实际上有一点区别。请参阅此article。我发现它非常有用,只是在这一点上我不同意并且更喜欢直接返回 Task 而不是等待。 (正如您在评论中建议的那样)
-
如果您只有一系列同步方法,您可以在异步方法(例如异步控制器方法、测试方法等)中使用模式
await Task.Run(() => { RunAnySynchronousMethod(); return Task.CompletedTask; });。
标签: c# asynchronous task async-await