我想问您关于何时使用正确架构的意见Task.Run
。我在WPF .NET 4.5应用程序(使用Caliburn Micro框架)中遇到了滞后的UI。
基本上我在做(非常简化的代码片段):
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的线程工作
,还是只包装内部运行的受CPU约束的方法,Task.Run
因为这使其可以在其他地方重用?我不确定在内核中深入后台线程是否是一个好主意。
广告(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)ConfigureAwait(false)
可以的话使用。
例如,await MyAsync().ConfigureAwait(false);
而不是await MyAsync();
。
ConfigureAwait(false)
告诉await
您不需要在当前上下文上恢复(在这种情况下,“在当前上下文上”表示“在UI线程上”)。但是,对于该async
方法的其余部分(之后ConfigureAwait
),您不能做任何假设您处于当前上下文中的操作(例如,更新UI元素)。
有关更多信息,请参见我的MSDN文章异步编程最佳实践。
2)Task.Run
用于调用CPU绑定方法。
您应该使用Task.Run
,但不要在要重用的任何代码(即库代码)中使用。所以你使用Task.Run
来调用该方法,而不是作为的一部分执行的方法。
因此,纯粹受CPU约束的工作如下所示:
// Documentation: This method is CPU-bound.
void DoWork();
您会使用Task.Run
以下命令致电:
await Task.Run(() => DoWork());
方法是一个混合的CPU绑定和I / O密集型应该有一个Async
与文件指出他们的CPU绑定性质签名:
// Documentation: This method is CPU-bound.
Task DoWorkAsync();
您还可以使用调用Task.Run
它(因为它部分受CPU限制):
await Task.Run(() => DoWorkAsync());
感谢您的快速回复!我知道您发布的链接,并看到了博客中引用的视频。实际上,这就是为什么我发布此问题的原因-在视频中说(与您的回复相同),您不应在核心代码中使用Task.Run。但是我的问题是,我每次使用它时都需要包装这种方法,以不减慢响应速度(请注意,我所有的代码都是异步的并且没有阻塞,但是如果没有Thread.Run,它就很麻烦)。我是否是只包装CPU绑定方法(许多Task.Run调用)还是将所有内容完全包装在一个Task.Run中是更好的方法?
您的所有库方法都应使用
ConfigureAwait(false)
。如果您首先这样做,那么您可能会发现Task.Run
完全没有必要。如果你做还是需要Task.Run
的话,并没有太大的区别,以运行在这种情况下,你是否叫过一次或多次,所以才做什么是最自然的,你的代码。我不了解第一种技术将如何帮助他。即使您使用
ConfigureAwait(false)
cpu绑定方法,仍然是UI线程将执行cpu绑定方法,并且只有之后的所有操作都可以在TP线程上完成。还是我误会了什么?@ user4205580:否,
Task.Run
了解异步签名,因此直到完成才DoWorkAsync
完成。多余的async
/await
是不必要的。我将在有关Task.Run
礼节的博客系列中更多地解释“为什么” 。@ user4205580:否。绝大多数“核心”异步方法不在内部使用它。在正常实施的“核心”异步方法的方式是使用
TaskCompletionSource<T>
或其速记符号之一如FromAsync
。我有一篇博客文章,其中详细介绍了为什么异步方法不需要线程。