正确使用 Task.Run 和 async-await 时 [英] When correctly use Task.Run and when just async-await

查看:88
本文介绍了正确使用 Task.Run 和 async-await 时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想就何时使用 Task.Run 的正确架构征求您的意见.我在 WPF .NET 4.5 中遇到了滞后的 UI应用程序(使用 Caliburn Micro 框架).

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();
    }
}

从我阅读/看到的文章/视频中,我知道 await async 不一定在后台线程上运行,要在后台开始工作,您需要用 await Task.Run(async () => ... ) 包裹它.使用 async await 不会阻塞 UI,但它仍然在 UI 线程上运行,所以它会变得滞后.

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.

在哪里放置 Task.Run 的最佳位置?

Where is the best place to put Task.Run?

我该不该

  1. 包装外部调用,因为这对于 .NET 来说是更少的线程工作

  1. Wrap the outer call because this is less threading work for .NET

,还是我应该只包装与 Task.Run 一起在内部运行的 CPU 绑定方法,因为这使其可在其他地方重用?我不确定在核心深处开始后台线程工作是否是个好主意.

, 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.

广告(1),第一个解决方案是这样的:

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.

广告(2),第二种解决方案是这样的:

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
}

推荐答案

注意 在 UI 线程上执行工作的指南,收集在我的博客中:

Note the guidelines for performing work on a UI thread, collected on my blog:

  • 每次阻塞 UI 线程的时间不要超过 50 毫秒.
  • 您可以每秒在 UI 线程上安排约 100 个延续;1000 太多了.

您应该使用两种技术:

1) 尽可能使用 ConfigureAwait(false).

例如,await MyAsync().ConfigureAwait(false); 而不是 await MyAsync();.

ConfigureAwait(false) 告诉 await 您不需要在当前上下文上恢复(在这种情况下,在当前上下文上"意味着在用户界面线程").但是,对于 async 方法的其余部分(在 ConfigureAwait 之后),您不能做任何假设您处于当前上下文中的事情(例如,更新 UI 元素).

ConfigureAwait(false) tells the await that you do not need to resume on the current context (in this case, "on the current context" means "on the UI thread"). However, for the rest of that async method (after the ConfigureAwait), you cannot do anything that assumes you're in the current context (e.g., update UI elements).

有关详细信息,请参阅我的 MSDN 文章异步编程的最佳实践.

For more information, see my MSDN article Best Practices in Asynchronous Programming.

2) 使用 Task.Run 调用 CPU 绑定的方法.

2) Use Task.Run to call CPU-bound methods.

您应该使用 Task.Run,但不要在您想要重用的任何代码(即库代码)中使用.因此,您使用 Task.Run调用该方法,而不是作为该方法实现的一部分.

You should use Task.Run, but not within any code you want to be reusable (i.e., library code). So you use Task.Run to call the method, not as part of the implementation of the method.

所以纯粹受 CPU 限制的工作看起来像这样:

So purely CPU-bound work would look like this:

// Documentation: This method is CPU-bound.
void DoWork();

您将使用 Task.Run 调用:

await Task.Run(() => DoWork());

混合受 CPU 限制和受 I/O 限制的方法应该具有 Async 签名,并带有指出其受 CPU 限制性质的文档:

Methods that are a mixture of CPU-bound and I/O-bound should have an Async signature with documentation pointing out their CPU-bound nature:

// Documentation: This method is CPU-bound.
Task DoWorkAsync();

您也可以使用 Task.Run 调用(因为它部分受 CPU 限制):

Which you would also call using Task.Run (since it is partially CPU-bound):

await Task.Run(() => DoWorkAsync());

这篇关于正确使用 Task.Run 和 async-await 时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆