2014-10-05 4 views
1

У меня вопрос об асинхронных операциях в C#. Предположим, у меня есть такой код:Асинхронные загрузки файлов в C#

public async void Download(string[] urls) 
{ 
    for(int i = 0; i < urls.Length; i++); 
    { 
     await DownloadHelper.DownloadAsync(urls[i], @"C:\" + i.ToString() + ".mp3"); 
    } 
} 

Но этот код на самом деле не загружает файлы асинхронно. Он начинает загружать файл с первого URL-адреса и затем ожидает эту операцию. Затем он начинает загружать файл со второго URL ... и так далее.

Таким образом, файлы загружаются один за другим, и я хотел бы, чтобы они начали загрузку одновременно.

Как я мог это сделать?

ответ

9

Когда вы говорите, вы имеете в виду асинхронной одновременно, они не являются тоже самое. Вы можете использовать Task.WhenAll для await для всех асинхронных операций одновременно:

public async Task Download(string[] urls) 
{ 
    var tasks = new List<Task>(); 
    for(int i = 0; i < urls.Length; i++); 
    { 
     tasks.Add(DownloadHelper.DownloadAsync(urls[i], @"C:\" + i.ToString() + ".mp3")); 
    } 

    await Task.WhenAll(tasks); 
} 

Вы также должны воздерживаться от использования async void, если в качестве обработчика событий

+0

Я часто возвращаю 'void' из методов' async', но только если я пропускаю метод «CancellationToken» для асинхронного метода (т. е. все еще есть дескриптор «забытого» метода) – spender

+4

@spender, но этот токен ничего не говорит вам. Вы не знаете, закончилась ли операция или существует ли исключение. [Вы должны избегать «async void» (http://msdn.microsoft.com/en-us/magazine/jj991977.aspx) – i3arnon

+0

... назовем это плохой привычкой;) – spender

4

Вместо того ожидают отдельные задачи, создавать задачи, не дожидаясь их (наклеить их в списке), а затем ждать Task.WhenAll вместо ...

public async void Download(string[] urls) 
{ 
    //you might want to raise the connection limit, 
    //in case these are all from a single host (defaults to 6 per host) 
    foreach(var url in urls) 
    { 
     ServicePointManager 
      .FindServicePoint(new Uri(url)).ConnectionLimit = 1000; 
    } 
    var tasks = urls 
     .Select(url => 
      DownloadHelper.DownloadAsync(
       url, 
       @"C:\" + i.ToString() + ".mp3")) 
     .ToList(); 
    await Task.WhenAll(tasks); 
} 
+0

Спасибо вам большое! – Junior222

+3

Вы действительно не должны делать «async void» здесь, так как я очень сомневаюсь, что «Download» - это обработчик событий. Не существует надежного способа получить уведомление о том, что исключение было нечестивым, когда вы возвращаете 'void'. Он будет пойман только «AppDomain.UnhandledException» или любым специальным обработчиком, который имеет «SynchronizationContext», если он существует (например, «Application.UnhandledException»). –

Смежные вопросы