2016-12-25 4 views
3

У меня есть клиент Windows-консоли, который спит в течение нескольких минут и при пробуждении запрашивает новые обновления из Rest API. Запрос генерирует ответ с данными json, включающий список объектов, каждый объект состоит из идентификатора, описания, снимка экрана, отправленного из API клиенту в качестве URL-адреса. консольное приложение требуется для использования ответа json, и для каждого объекта он просматривает URL-адрес и пытается загрузить соответствующее изображение, связанное с каждым объектом в списке. Код выражается следующим образом:Как добиться загрузки файла Async с помощью TPL в C#

foreach (var jobject in response) 
{ 
    Console.WriteLine(jobject.id); 
    Console.WriteLine(jobject.description); 
    if (jobject.shotUrl != null) 
    { 
     WebClient webclient = new WebClient(); 
     webclient.DownloadFileAsync(new System.Uri(jobject.shotUrl), "F:\\" + jobject.id + ".jpg"); 
    } 
} 

Иногда может быть около 500 объектов JSON, что означает 500 фото скачать ... снова означает создание 500 web-клиентов. Я чувствую, что это не очень хорошая идея.

Мой вопрос: могу ли я повысить производительность, если я полагаюсь на TPL? как это сделать?

+3

Ваш вопрос противоречит самому себе. Вы хотите больше * производительности * или хотите меньше использования ресурсов *? – zaitsman

+0

Имейте в виду, что на конечной точке установлен лимит подключения по умолчанию, в консольном приложении - 2 IIRC. Это означает, что вы никогда не сможете скачивать больше, чем те 2 одновременно. см. https://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.defaultconnectionlimit(v=vs.110).aspx – rene

+2

@rene в этом вопросе есть ответ, показывающий, как увеличить параллельные соединения http://stackoverflow.com/questions/26206412/asynchronous-downloading-files-in-c-sharp – Alrehamy

ответ

1

Использование HttpClient гораздо проще для параллельности, вы можете объявить один частный HttpClient для класса:

System.Net.Http.HttpClient _client = new System.Net.Http.HttpClient(); 

И тогда вы можете написать метод, который загружает файлы и сохраняет их на диск, как так:

private async Task DownloadFile(string shortUrl, string destination) 
{ 
    using (var response = await _client.GetStreamAsync(shortUrl)) 
    using (var fileStream = File.Create(destination)) 
    { 
     await response.CopyToAsync(fileStream); 
     await fileStream.FlushAsync(); 
    } 
} 

Затем вы можете использовать его как это:

try 
{ 
    await DownloadFile(jobject.shortUrl, "F:\\" + jobject.id + ".jpg"); 
} 
catch (Exception e) 
{ 
    // Do appropriate exception handling 
} 

И если вы тусклый t, чтобы загрузить все файлы параллельно. Вы можете использовать Task.WhenAll()

try 
{ 
    var tasks = response.Select(j => DownloadFile(j.shortUrl, "F:\\" + j.id + ".jpg")); 
    await Task.WhenAll(tasks); 
} 
catch (Exception e) 
{ 
    // Do appropriate exception handling 
} 
+1

Я сравнивал ваш код, его производительность теперь хуже, чем моя оригинальная работа. – Alrehamy

+0

Ну, честно говоря, я вообще не запускал код, потому что мне не хватало контекста (например, что такое 'response'). Но я уверен, что немного асинхронный код (особенно Task.WhenAll один) обеспечит лучшую производительность. – Encrypt0r

+0

Загрузка файлов параллельно должна выполняться также с помощью 'async':' async j => ждать DownloadFile', а не только 'j => DownloadFile', потому что во втором случае это делается одновременно и синхронно. – VMAtm