嵌套的异步下载 - 异步内的异步
我有一些嵌套的异步方法互相调用,它很混乱。我正试图转换一个下载异步下载文件的项目。 下载按钮的点击,这是触发方法:嵌套的异步下载 - 异步内的异步
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("'", "'");
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
下载和男孩,我已经远离我的极限了。
我在哪里搞砸了?主要问题是,它会按照它应该工作的方式工作吗?
您的代码看起来相当不错,除了两件事情:
- 你不应该使用
Task.Run
。Task.Run
的主要用例是将CPU绑定的工作从GUI线程移出,因此它不会阻塞UI。我在proper use ofTask.Run
上有一系列详细的介绍。 - 你应该使用一致的异步模式,最好是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("'", "'");
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;
}
}
在发布之前,我没有使用'Task.Run'。尽管我没有,但VS2015总是建议我使用它。这就是我为什么这么做的原因。这可能是一种方法的小故障。感谢您的帮助。我一定会看看上面提供的链接。 –
@JohnP .:我认为你得到的消息是“考虑使用await操作符来等待不阻塞的API调用,或者”等待Task.Run“在后台线程上执行CPU绑定的工作。我们在这里并没有进行CPU绑定的工作。如果您从“外部输入”中应用异步,那么您只会收到此消息,这与使最低级别的API调用首先等待并让它从此发展的更自然的方法相反。 –
有太多的代码在这里,仍然不够。请提供一个很好的[mcve],可以可靠地再现您的问题。同时,我会指出使用'Task.Run()'执行'async'方法通常毫无意义。只需直接调用'async'方法并'等待'返回的'Task'对象。您可以通过将返回的“Task”表示为某个“IEnumerable”来收集一组'async'调用,然后使用'await Task.WhenAll(...)'等待整个集合。 –
如果您更仔细地研究'async' /'await'的现有引用并使用好的习语,那么我敢打赌,你的问题将会消失。总之,请注意,如果您异步执行多个操作,那么完全按照与启动时不同的顺序完成并不奇怪。如果你使用异步操作,否则,你可能只是一次一个地同步执行它们。 –
你不需要将你的异步方法包装在Task.Run中。这不是必需的,它会让你的代码更难阅读。您可以等待任务返回异步方法。执行顺序不同的原因也是因为您在启动下载的Task.Run中触发了下载。然后该方法返回并开始下一次下载。完成的事件被激发的顺序取决于请求时间本身。如果你真的想保持顺序,你应该使用DownloadStringTaskAsync,而这会返回一个任务,你可以等待结果。 –