嵌套的异步下载 - 异步内的异步

问题描述:

我有一些嵌套的异步方法互相调用,它很混乱。我正试图转换一个下载异步下载文件的项目。 下载按钮的点击,这是触发方法:嵌套的异步下载 - 异步内的异步

private async void enableOfflineModeToolStripMenuItem_Click(object sender, EventArgs e) 
     { 
      for(int i = 0; i < _playlists.Count; i++) 
      { 
       DoubleDimList.Add(new List<String>()); 
       for(int j = 0; j < 5; j++) 
       { 
        string sMp3 = IniReadValue(_playlists[i], "Track " + j); 
        DoubleDimList[i].Add(sMp3); 
       } 
       await Task.Run(() => _InetGetHTMLSearchAsyncs(DoubleDimList[i]));    
      } 
     } 

它创建了一个2d List其在最后看起来像这样DoubleDimList[3][20]。 在每个sublist的末尾,我正在做一个async下载,如你所见。该方法是这样的

private async Task _InetGetHTMLSearchAsyncs(List<string> urlList) 
     { 
      foreach (var url in urlList) 
      { 
       await Task.Run(() => _InetGetHTMLSearchAsync(url)); 
      } 
     } 

_InetGetHTMLSearchAsync方法是这样的,在这里是它得到棘手

private async Task _InetGetHTMLSearchAsync(string sTitle) 
     { 
      Runs++; 
      if (AudioDumpQuery == string.Empty) 
      { 
       //return string.Empty; 
      } 
      string sResearchURL = "http://www.audiodump.biz/music.html?" + AudioDumpQuery + sTitle.Replace(" ", "+"); 
      try 
      { 
       using (var client = new WebClient()) 
       { 
        client.Headers.Add("Referer", @"http://www.audiodump.com/"); 
        client.Headers.Add("user-agent", "Mozilla/5.0(Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14(KHTML, like Gecko) Version/7.0.3 Safari/7046A194A"); 
        client.DownloadStringCompleted += Client_DownloadStringCompleted; 
        await Task.Run(() => client.DownloadStringAsync(new Uri(sResearchURL))); 

       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine("Debug message: " + ex.Message + "InnerEx: " + ex.StackTrace); 
       Console.WriteLine("Runs: " + Runs); 
       return; 
      } 
     } 

Client_DownloadStringCompleted还有另一种方法async叫。这里是

private async void Client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) 
     { 
      string[] sStringArray; 
      string aRet = e.Result; 
      string[] aTable = _StringBetween(aRet, "<BR><table", "table><BR>", RegexOptions.Singleline); 
      if (aTable != null) 
      { 
       string[] aInfos = _StringBetween(aTable[0], ". <a href=\"", "<a href=\""); 
       if (aInfos != null) 
       { 
        for (int i = 0; i < 1; i++) 
        { 
         sStringArray = aInfos[i].Split('*'); 
         sStringArray[0] = sStringArray[0].Replace("&#39;", "'"); 
         aLinks.Add(sStringArray[0]); 
        } 
        await Task.Run(() => DownloadFile(aLinks[FilesDownloaded])); 
       } 
      } 
     } 

从那里,惊喜!拨打另一个async

private async Task DownloadFile(string url) 
     { 
      try 
      { 
       using (var client = new WebClient()) 
       { 
        client.Headers.Add("Referer", @"http://www.audiodump.biz/"); 
        client.Headers.Add("user-agent", "Mozilla/5.0(Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14(KHTML, like Gecko) Version/7.0.3 Safari/7046A194A"); 
        client.DownloadFileCompleted += Client_DownloadFileCompleted; 
        await Task.Run(() => client.DownloadFileTaskAsync(url, mp3Path + "\\" + count + ".mp3")); 

       } 
      } 
      catch (Exception Ex) 
      { 
       Console.WriteLine("File download error: " + Ex.StackTrace); 
      } 
     } 

现在创建2d List后,第一部分是检索的MP3下载链接。第二部分是在提供有效的URL后立即下载mp3。它的工作原理是奇怪的。它通常不会下载文件(第1,第2,第3 ...),它会随机下载文件(第1,第5,第8 ...)。

这是我第一次去async下载和男孩,我已经远离我的极限了。

我在哪里搞砸了?主要问题是,它会按照它应该工作的方式工作吗?

+1

有太多的代码在这里,仍然不够。请提供一个很好的[mcve],可以可靠地再现您的问题。同时,我会指出使用'Task.Run()'执行'async'方法通常毫无意义。只需直接调用'async'方法并'等待'返回的'Task'对象。您可以通过将返回的“Task”表示为某个“IEnumerable ”来收集一组'async'调用,然后使用'await Task.WhenAll(...)'等待整个集合。 –

+0

如果您更仔细地研究'​​async' /'await'的现有引用并使用好的习语,那么我敢打赌,你的问题将会消失。总之,请注意,如果您异步执行多个操作,那么完全按照与启动时不同的顺序完成并不奇怪。如果你使用异步操作,否则,你可能只是一次一个地同步执行它们。 –

+0

你不需要将你的异步方法包装在Task.Run中。这不是必需的,它会让你的代码更难阅读。您可以等待任务返回异步方法。执行顺序不同的原因也是因为您在启动下载的Task.Run中触发了下载。然后该方法返回并开始下一次下载。完成的事件被激发的顺序取决于请求时间本身。如果你真的想保持顺序,你应该使用DownloadStringTaskAsync,而这会返回一个任务,你可以等待结果。 –

您的代码看起来相当不错,除了两件事情:

  1. 你不应该使用Task.RunTask.Run的主要用例是将CPU绑定的工作从GUI线程移出,因此它不会阻塞UI。我在proper use of Task.Run上有一系列详细的介绍。
  2. 你应该使用一致的异步模式,最好是TAP。您的代码当前正在使用TAP无处不在,除了_InetGetHTMLSearchAsync,这是使用EAP。这是什么导致你看到奇怪的行为。

固定_InetGetHTMLSearchAsync会是这个样子:

private async Task _InetGetHTMLSearchAsync(string sTitle) 
{ 
    Runs++; 
    string sResearchURL = "http://www.audiodump.biz/music.html?" + AudioDumpQuery + sTitle.Replace(" ", "+"); 
    try 
    { 
    using (var client = new WebClient()) 
    { 
     client.Headers.Add("Referer", @"http://www.audiodump.com/"); 
     client.Headers.Add("user-agent", "Mozilla/5.0(Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14(KHTML, like Gecko) Version/7.0.3 Safari/7046A194A"); 
     string[] sStringArray; 
     string aRet = await client.DownloadStringTaskAsync(new Uri(sResearchURL)); 
     string[] aTable = _StringBetween(aRet, "<BR><table", "table><BR>", RegexOptions.Singleline); 
     if (aTable != null) 
     { 
     string[] aInfos = _StringBetween(aTable[0], ". <a href=\"", "<a href=\""); 
     if (aInfos != null) 
     { 
      for (int i = 0; i < 1; i++) 
      { 
      sStringArray = aInfos[i].Split('*'); 
      sStringArray[0] = sStringArray[0].Replace("&#39;", "'"); 
      aLinks.Add(sStringArray[0]); 
      } 
      await DownloadFile(aLinks[FilesDownloaded]); // Should really be called "DownloadFileAsync" 
     } 
     } 
    } 
    } 
    catch (Exception ex) 
    { 
    Console.WriteLine("Debug message: " + ex.Message + "InnerEx: " + ex.StackTrace); 
    Console.WriteLine("Runs: " + Runs); 
    return; 
    } 
} 
+0

在发布之前,我没有使用'Task.Run'。尽管我没有,但VS2015总是建议我使用它。这就是我为什么这么做的原因。这可能是一种方法的小故障。感谢您的帮助。我一定会看看上面提供的链接。 –

+1

@JohnP .:我认为你得到的消息是“考虑使用await操作符来等待不阻塞的API调用,或者”等待Task.Run“在后台线程上执行CPU绑定的工作。我们在这里并没有进行CPU绑定的工作。如果您从“外部输入”中应用异步,那么您只会收到此消息,这与使最低级别的API调用首先等待并让它从此发展的更自然的方法相反。 –