在lambda函数内部再次使用async await

3
我正在阅读关于async & await的几篇博客,尤其是Scott的博客。下面有一个代码示例,它可能会下载一系列URL。为了简化事情并使时间合理且可重复,我用Task.Delay可等待调用替换了真正的下载代码。
第一个代码在lambda表达式中没有async-await对,而第二个代码有。两个代码都编译,时间都相同(大约1秒)。
1.) 哪种方法是正确的?
2.) lambda内部的await async对是否会增加成本?
private async void Button_Click(object sender, RoutedEventArgs e)
{
  // Capture the UI synchronization context for use later
  var ui = TaskScheduler.FromCurrentSynchronizationContext();

  // SAMPLE 1 , is this the right way?
  var items = Enumerable.Range(1, 100).Select(i => i.ToString()).ToList();
  var sp = new Stopwatch();
  sp.Start();
  // NO await async pair in lambda
  var results1 = await Task.WhenAll(
                            items.Select(item => DownloadFileAsync(item)));
  sp.Stop();
  var testResult = string.Format("Single await: {0} seconds"
                                   , sp.Elapsed.TotalSeconds);
  // SAMPLE 2, or this way?
  var items1 = Enumerable.Range(1, 100).Select(i => i.ToString()).ToList();

  var sp1 = new Stopwatch();
  sp1.Start();
  // WITH await async pair in lambda
  var results = await Task.WhenAll(items1.Select(async item => 
                                      await DownloadFileAsync(item)));
  sp1.Stop();
  var testResult1 = string.Format("Double await: {0} seconds", 
                                      sp1.Elapsed.TotalSeconds);
  // show results
  await Task.Factory.StartNew(() =>
  {
     MessageBox.Show(testResult + System.Environment.NewLine + testResult1);
  }, CancellationToken.None, TaskCreationOptions.None, 
                                ui).ConfigureAwait(false);    
}

并且

private async Task<string> DownloadFileAsync(string uri)
{
    //using (var client = new WebClient())
    //{
    //  string data = await 
        client.DownloadStringTaskAsync(uri).ConfigureAwait(false);
    //  return data;
    //}
    
   await Task.Delay(1000).ConfigureAwait(false);
   return uri;        
}

1个回答

4
两种写法大致相同,第一种写法稍微更有效率。
对于本问题,您可以将
- await 视为“解开”一个任务 (task),并且 - async 视为在任务(task)中“包装”一个方法。
第一个例子使用 lambda 表达式来定义一个带有签名 Task<string> F(string) 的函数。第二个例子定义的函数拥有相同的签名,不同之处在于:
- 第一个例子直接从 DownloadFileAsync 返回任务 (task) - 而第二个例子则是先解开该任务,再将其包装起来。

有没有其他的方式可以“可视化”地看到区别?例如,查看IL代码? - alpinescrambler
@alpinescrambler:是的,编译器生成的代码非常不同。 - Stephen Cleary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接