Некоторые предыстории.
У меня есть список из 5000 фотографий с https://jsonplaceholder.typicode. Конечная точка .com/photos, и я хочу загрузить эти фотографии (они отбрасываются, но по сути имитируют загрузку). Я пытаюсь сделать это, используя разные подходы, и выяснить, сколько времени занимает каждый подход и почему.
- Первый подход, последовательный, загружает один фото за другим и занимает больше всего времени (около 24 минут). Это и понятно, так как я жду полной загрузки предыдущей фотографии, пока не начнется следующая. Так что никаких претензий.
- Второй подход использует List, добавляет каждую задачу загрузки фотографий в список и, наконец, ожидает для выполнения всех задач. Это занимает примерно 1 минуту 7 секунд. Поскольку он загружает несколько фотографий параллельно, мы ожидали меньшего времени по сравнению с первым, то есть последовательным подходом, и это так.
. - Третий подход использует Parallel.ForEachAsync(). К моему удивлению, загрузка всех фотографий заняла 5 минут 19 секунд. Я ожидал, что этот подход будет аналогичен второму подходу, однако это не так.
Код: Выделить всё
using System.Diagnostics;
using System.Text.Json;
var httpClient = new HttpClient();
var photosResponse = await httpClient.GetAsync("https://jsonplaceholder.typicode.com/photos");
var content = await photosResponse.Content.ReadAsStringAsync();
var photos = JsonSerializer.Deserialize(content, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
})!;
var stopwatch = new Stopwatch();
stopwatch.Start();
// // 1.Sequential - Time taken: 23m 58s
// foreach (var photo in photos)
// {
// Console.WriteLine($"Downloading {photo.Id} on Thread {Environment.CurrentManagedThreadId}");
// var imageResponse = await httpClient.GetAsync(photo.Url);
// _ = await imageResponse.Content.ReadAsByteArrayAsync();
// Console.WriteLine($"Downloaded {photo.Id} on Thread {Environment.CurrentManagedThreadId}");
// }
// 2.Tasks - Time taken: 1m 7s
var tasks = new List();
foreach (var photo in photos!)
{
tasks.Add(DownloadPhotoTask(photo, httpClient));
}
await Task.WhenAll(tasks);
// // 3.Parallel.ForEach - Time taken: 5m 19s
// await Parallel.ForEachAsync(photos, (photo, _) => DownloadPhotoValueTask(photo, httpClient));
stopwatch.Stop();
Console.WriteLine($"Time elapsed: {stopwatch.Elapsed}");
return;
async Task DownloadPhotoTask(Photo photo, HttpClient httpClientInternal)
{
Console.WriteLine($"Downloading {photo.Id} on Thread {Environment.CurrentManagedThreadId}");
var imageResponse = await httpClientInternal.GetAsync(photo.Url);
_ = await imageResponse.Content.ReadAsByteArrayAsync();
Console.WriteLine($"Downloaded {photo.Id} on Thread {Environment.CurrentManagedThreadId}");
}
async ValueTask DownloadPhotoValueTask(Photo photo, HttpClient httpClientInternal)
{
Console.WriteLine($"Downloading {photo.Id} on Thread {Environment.CurrentManagedThreadId}");
var imageResponse = await httpClientInternal.GetAsync(photo.Url);
_ = await imageResponse.Content.ReadAsByteArrayAsync();
Console.WriteLine($"Downloaded {photo.Id} on Thread {Environment.CurrentManagedThreadId}");
}
public record Photo(int Id, string Title, string Url);
Кроме того, если бы вы могли подробно рассказать о внутренней работе второго и третьего подходов, это было бы полезно.
Подробнее здесь: https://stackoverflow.com/questions/784 ... st-and-why