什么是执行顺序/逻辑:Task.Run(async()=>等待someAsyncMethod();)

时间:2018-07-27 12:53:26

标签: c# asynchronous task

阅读时 How to: Create Pre-Computed Tasks 示例方法DownloadStringAsync返回

Task.Run( async ()=> { return await new WebClient().DownloadStringTaskAsync(address)}) 

我想知道为什么我们需要在Task.Run()中包装一个异步方法吗? WebClient().DownloadStringTaskAsync()方法本身会返回Task

2 个答案:

答案 0 :(得分:4)

我认为问题在于,他们想展示如何使用Task.FromResult,然后因需要消耗Task返回方法而陷入困境。

如今,编写消耗Task返回方法的代码的自然方法是使它们成为async。但是,如果这样做,Task.FromResult就会消失:

   // Asynchronously downloads the requested resource as a string.
   public static async Task<string> DownloadStringAsync(string address)
   {
      // First try to retrieve the content from cache.
      string content;
      if (cachedDownloads.TryGetValue(address, out content))
      {
         return content;
      }

      content = await new WebClient().DownloadStringTaskAsync(address);
      cachedDownloads.TryAdd(address, content);
      return content;
   }

简单的代码,仍然可以达到总体目标。除非您期望cachedDownloads.TryAdd占用大量CPU资源,否则在这种情况下,其版本也会保证将其推送到线程池中运行。

简而言之-不要复制此代码,这不是从 2 开始工作的示例


此版本可避免在不需要时避免分配async状态机 1 ,显示Task.FromResult并且仍然不使用Task.Run >

   // Asynchronously downloads the requested resource as a string.
   public static Task<string> DownloadStringAsync(string address)
   {
      // First try to retrieve the content from cache.
      string content;
      if (cachedDownloads.TryGetValue(address, out content))
      {
         return Task.FromResult(content);
      }

      return DownloadStringSlowAsync(address);
   }
   private static async Task<string> DownloadStringSlowAsync(string address)
   {
      string content = await new WebClient().DownloadStringTaskAsync(address);
      cachedDownloads.TryAdd(address, content);
      return content;
   }

更好:(不,这不是一个字,我不在乎)

   static ConcurrentDictionary<string, Task<string>> cachedDownloads =
   new ConcurrentDictionary<string, Task<string>>();
   // Asynchronously downloads the requested resource as a string.
   public static Task<string> DownloadStringAsync(string address)
   {
      // First try to retrieve the content from cache.
      Task<string> content;
      if (cachedDownloads.TryGetValue(address, out content))
      {
         return content;
      }

      return DownloadStringSlowAsync(address);
   }
   private static async Task<string> DownloadStringSlowAsync(string address)
   {
      string content = await new WebClient().DownloadStringTaskAsync(address);
      cachedDownloads.TryAdd(address, Task.FromResult(content));
      return content;
   }

因为现在我们的缓存仅包含完成的任务,我们可以一遍又一遍地分发它们,而不必在每个请求上重复分配新的Task对象。

当然,只有当缓存的对象(此处为string不可变的时,这些方法中的任何一种才真正可行。


1 不要自动执行此操作。应该基于这样的分配是否引起绩效问题来进行深思熟虑的决定。

2 这也是 一个不好的缓存示例,因为正如Raymond Chen指出的那样,A cache with a bad policy is another name for a memory leak。在此示例中,根本没有 有效期。

答案 1 :(得分:2)

TL; DR不要使用该示例。除了作为智力锻炼。实际上,它的当前状态表明未完成,Robust Programming部分为空

长版

我们没有,这个例子是人为的。

实际的代码不是只是调用DownloadStringTaskAsync()。实际的代码是:

static ConcurrentDictionary<string, string> cachedDownloads =
  new ConcurrentDictionary<string, string>();

// Asynchronously downloads the requested resource as a string.
public static Task<string> DownloadStringAsync(string address)
{
  // First try to retrieve the content from cache.
  string content;
  if (cachedDownloads.TryGetValue(address, out content))
  {
     return Task.FromResult<string>(content);
  }

  // If the result was not in the cache, download the 
  // string and add it to the cache.
  return Task.Run(async () =>
  {
     content = await new WebClient().DownloadStringTaskAsync(address);
     cachedDownloads.TryAdd(address, content);
     return content;
  });
}

这是一个非常具体的示例,需要返回一个保证结果已被缓存的任务。

该函数使HTTP调用与缓存异步。这意味着它必须返回一个任务。

首先,它检查结果是否已经可用。如果是,它将包裹在完整的Task中:

string content;
if (cachedDownloads.TryGetValue(address, out content))
{
   return Task.FromResult<string>(content);
}

否则,它将返回一个正在运行的任务,该任务会进行HTTP调用,并确保返回结果之前对其进行缓存:

  // If the result was not in the cache, download the 
  // string and add it to the cache.
  return Task.Run(async () =>
  {
     content = await new WebClient().DownloadStringTaskAsync(address);
     cachedDownloads.TryAdd(address, content);
     return content;
  });

该文章有些虚构,但具有其他优点

本文试图说明如何使用已完成的任务,尽管该示例并没有那么清晰。它具有与主题没有直接关系的好处。

例如,他们避免使用async/await并通过在最后返回正在运行的任务来避免这种花费。除非缓存中缺少结果,否则编译器将生成async/await方法所需的异步状态机。

这意味着更少的分配和更少的IL复杂度

但是随后他们去使用效率较低的WebClient类...。