Асинхронно добавлять элементы в список в foreach l oop - PullRequest
0 голосов
/ 05 мая 2020

У меня есть al oop, который извлекает объекты из стороннего API (поэтому я должен запрашивать каждый объект по одному) и добавляет их в список, который я затем возвращаю из своей процедуры. В настоящее время он выполняет это последовательно, но я бы хотел, чтобы l oop был асинхронным для повышения производительности.

Код basi c выглядит следующим образом:

public async Task<List<ResponseObject<MyClass>>> GetMyClass(string[] references)
    {
        var responseObject = new ResponseObject<MyClass>();
        var responseObjects = new List<ResponseObject<MyClass>>();

            foreach (var reference in references)
            {
                responseObject = await GetExternalData(reference);
                responseObjects.Add(responseObject);
            }

        return responseObjects;
    }

Метод I call определяется следующим образом:

public async Task<ResponseObject<MyClass>> GetExternalData(string reference)

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

1 Ответ

4 голосов
/ 05 мая 2020

Попробуйте сделать это с помощью .Select:

public async Task<List<ResponseObject<MyClass>>> GetMyClass(string[] references)
{
    var responseObject = new ResponseObject<MyClass>();
    var responseObjects = new ConcurrentBag<ResponseObject<MyClass>>();

    var tasks = references.Select(async item => 
    {
        var responseObject = await GetExternalData(item);
        responseObjects.Add(responseObject);
    });

    await Task.WhenAll(tasks);

    return responseObjects.ToList();
}

Существует также пакет AsyncEnumerator NuGet, который включает ParallelForEachAsync, который работает очень аналогично Parallel.ForEach. Я бы рекомендовал использовать этот подход, если references может быть довольно большим количеством объектов. Как написано выше, это вызовет столько задач, сколько имеется references. Используя ParallelForEachAsync, вы можете контролировать степень параллелизма, чтобы избежать слишком большого количества запросов, это будет выглядеть так:

public async Task<List<ResponseObject<MyClass>>> GetMyClass(string[] references)
{
    var responseObject = new ResponseObject<MyClass>();
    var responseObjects = new ConcurrentBag<ResponseObject<MyClass>>();

    await references.ParallelForEachAsync(async item =>
    {
        var responseObject = await GetExternalData(item);
        responseObjects.Add(responseObject);   
    }, maxDegreeOfParallelism: 8);

    return responseObjects.ToList();
}
...