2016-04-24 113 views
0

我有一些嵌套的異步方法互相調用,它很混亂。我正試圖轉換一個下載異步下載文件的項目。 下載按鈕的點擊,這是觸發方法:嵌套的異步下載 - 異步內的異步

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

您的代碼看起來相當不錯,除了兩件事情:

  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調用首先等待並讓它從此發展的更自然的方法相反。 –