Как запустить метод для всех элементов в списке C #, чтобы улучшить время загрузки - PullRequest
3 голосов
/ 11 марта 2019

У меня есть проект в работе, который требует выборки данных для более чем 300 пользователей через REST API, и пользователь не может продолжать использовать приложение должным образом, пока не будут получены все эти данные.В идеале я хотел бы запускать метод FetchUserDetails для каждого элемента в списке одновременно и обновлять каждую запись при возвращении подробностей.

Примечание. Чтобы предотвратить блокирование потока пользовательского интерфейса, эта функциональность вложена вФоновый рабочий.

Мой класс User выглядит примерно так:

public class User 
{
    public List<string> Details { get; set; }
}

Внутри фонового рабочего у меня есть следующие функциональные возможности:

List<User> users; // List of 300+ users
Parallel.ForEach(users, user =>
{
    user.Details = FetchUserDetails(user);
});

И метод FetchUserDetails,не вдаваясь в подробности из-за ограничений NDA:

private List<string> FetchUserDetails(User inputUser)
{
    // Long running, nested API calls that take over 50 seconds
    return List<string>;
}

Есть ли способ запустить метод для каждой из 300+ записей одновременно, чтобы значительно сократить время, необходимое для извлечения данных.

1 Ответ

1 голос
/ 12 марта 2019

Parallel.ForEach позволяет вам установить MaxDegreeOfParallelism

Parallel.ForEach(
    users, 
    new ParallelOptions { MaxDegreeOfParallelism = 300 },
    user => { user.Details = FetchUserDetails(user); }
);

Вы можете даже сделать -1, чтобы MaxDegreeOfParallelism означал неограниченный

Однако установка этого параметра только уменьшит число, которое будет использоваться. Подробнее см. этот документ .

По умолчанию For и ForEach будут использовать столько потоков, сколько обеспечивает базовый планировщик, поэтому изменение MaxDegreeOfParallelism по умолчанию ограничивает только количество одновременных задач.

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

Лучшим подходом было бы сделать вызовы асинхронными, чтобы после блокировки запроса в ожидании ответа исходный поток мог свободно запустить следующий запрос.

Что-то вроде:

async Task SetUserDetails(User user)
{
    user.Details = await FetchUserDetails(user);
}

async Task UpdateUsers(IEnumerable<User> users)
{
    var tasks = new List<Task>();
    foreach(var user in users)
    {
         tasks.Add(SetUserDetails(user));
    }
    await Task.WhenAll(tasks);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...