给定一个包含 Urls 的输入文本文件,我想一次下载相应的文件。我使用这个问题UserState的答案 使用 WebClient 和 TaskAsync 从 Async CTP 下载作为参考。
public void Run()
{
List<string> urls = File.ReadAllLines(@"c:/temp/Input/input.txt").ToList();
int index = 0;
Task[] tasks = new Task[urls.Count()];
foreach (string url in urls)
{
WebClient wc = new WebClient();
string path = string.Format("{0}image-{1}.jpg", @"c:/temp/Output/", index+1);
Task downloadTask = wc.DownloadFileTaskAsync(new Uri(url), path);
Task outputTask = downloadTask.ContinueWith(t => Output(path));
tasks[index] = outputTask;
}
Console.WriteLine("Start now");
Task.WhenAll(tasks);
Console.WriteLine("Done");
}
public void Output(string path)
{
Console.WriteLine(path);
}
Run Code Online (Sandbox Code Playgroud)
我预计文件的下载将在“Task.WhenAll(tasks)”点开始。但事实证明,输出看起来像
c:/temp/Output/image-2.jpg c:/temp/Output/image-1.jpg c:/temp/Output/image-4.jpg c:/temp/Output/image-6.jpg c:/temp/Output/image-3.jpg [删除了许多行] 现在开始 c:/temp/Output/image-18.jpg c:/temp/Output/image-19.jpg c:/temp/Output/image-20.jpg c:/temp/Output/image-21.jpg c:/temp/Output/image-23.jpg [删除了许多行] 完毕
为什么在调用 WaitAll 之前就开始下载?我可以改变什么来实现我想要的(即所有任务将同时开始)?
谢谢
为什么在调用 WaitAll 之前就开始下载?
首先,您不是在调用Task.WaitAll,它同步阻塞,而是调用Task.WhenAll,它返回一个应等待的可等待对象。
现在,正如其他人所说,当你调用一个异步方法时,即使没有使用await它,它也会触发异步操作,因为任何符合 TAP 的方法都会返回一个“热任务”。
我可以改变什么来实现我想要的(即所有任务将同时开始)?
现在,如果您想将执行推迟到Task.WhenAll,则可以使用Enumerable.Select将每个元素投影到 a Task,并在将其传递给 时将其具体化Task.WhenAll:
public async Task RunAsync()
{
IEnumerable<string> urls = File.ReadAllLines(@"c:/temp/Input/input.txt");
var urlTasks = urls.Select((url, index) =>
{
WebClient wc = new WebClient();
string path = string.Format("{0}image-{1}.jpg", @"c:/temp/Output/", index);
var downloadTask = wc.DownloadFileTaskAsync(new Uri(url), path);
Output(path);
return downloadTask;
});
Console.WriteLine("Start now");
await Task.WhenAll(urlTasks);
Console.WriteLine("Done");
}
Run Code Online (Sandbox Code Playgroud)