What is the execution order/logic : Task.Run( async() => await someAsyncMethod(); )

zhe*_* yu 4 c# asynchronous task

When reading How to: Create Pre-Computed Tasks the example method DownloadStringAsync returns

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

I wonder why we need to wrap a async method in a Task.Run()? the WebClient().DownloadStringTaskAsync() method returns a Task itself.

Dam*_*ver 5

我认为问题在于他们想展示如何使用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;
   }
Run Code Online (Sandbox Code Playgroud)

更简单的代码,仍然达到了总体目标。除非您希望cachedDownloads.TryAddCPU 占用很大,在这种情况下,他们的版本还保证将其推送到线程池中运行。

简而言之- 不要复制此代码,这不是从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;
   }
Run Code Online (Sandbox Code Playgroud)

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

   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;
   }
Run Code Online (Sandbox Code Playgroud)

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

当然,这些方法中的任何一种只有在缓存对象(string此处)是不可变的情况下才真正可行。


1不要自动执行此操作。这应该是基于此类分配是否会导致性能问题的深思熟虑的决定。

2也是缓存的一个不好的例子,因为正如 Raymond Chen 指出的那样,具有错误策略的缓存是内存泄漏的另一个名称。在这个例子中,根本没有过期。