Task.WhenAll против Parallel.ForEachAsync – какой подход лучше и почему?C#

Место общения программистов C#
Ответить
Anonymous
 Task.WhenAll против Parallel.ForEachAsync – какой подход лучше и почему?

Сообщение Anonymous »

Я пытаюсь понять, как работает библиотека потоков и параллельных задач в .NET. Итак, я экспериментировал с одновременным выполнением задач, используя два подхода, как показано ниже:
Некоторые предыстории.
У меня есть список из 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);
Может кто-нибудь помочь мне понять эту значительную разницу между временем, затраченным на второй и третий подходы? Как лучше? Если длительность является единственным параметром, то, очевидно, согласно моему тесту, лучшим подходом является второй. Если да, то зачем нам Parallel.ForEachAsync()?
Кроме того, если бы вы могли подробно рассказать о внутренней работе второго и третьего подходов, это было бы полезно.

Подробнее здесь: https://stackoverflow.com/questions/784 ... st-and-why
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»