#c# #async-await #batch-processing
#c# #асинхронный-ожидание #пакетная обработка
Вопрос:
Мне нужно разделить группу NET.Ядро 3.1 HttpClient (Assembly System.Net.Http, версия = 4.2.2.0) веб-запросы в пакетах по 10, дождитесь завершения этих 10 вызовов, сохраните результат где-нибудь и повторяйте, пока все пакеты не будут завершены.
Пример: у меня есть конечная точка, которая получает идентификатор и возвращает некоторый json взамен. У меня есть список с 25 идентификаторами пользователей, мне нужно разделить работу на 10 веб-запросов одновременно синхронно, до завершения, получения результата и перехода к следующему пакету (итак, 10, получаем результат, 10, получаем результат, последние 5, получаем результат)
Лучший способ пакетной обработки через Linq, который я нашел, — это использование этого кода для пакетной обработки в библиотеке MoreLINQ, поэтому в коде вы увидите «Пакетный» метод.
Я пришел к следующему:
//Pretend that HttpClient is already configured
//Concurrent queue to collect results from webservice, result order it's not i
var resultsCollection = new ConcurrentQueue<string>();
string inventedWebCall = @"http://fantasywebapi.com/searchById?";
//list of ids
var ids = Enumerable.Range(1, 25);
//Action that calls webrequest and enqueues result in thread safe queue
Action<int> webRequest = async (id) => {
var webCall = $"{inventedWebCall}{id}";
var webResult = await httpClient.GetAsync(new Uri(webCall));
if (webResult.IsSuccessStatusCode)
{
resultsCollection.Enqueue(await webResult.Content.ReadAsStringAsync())
}
};
//Gets a list of lists divided by 10 (10, 10, 5)
var batches = ids.Batch(10, batch => batch);
foreach (var batch in batches)
{
//Collects the result of concurrent webrequest calls
//Contains the tasks to await concurrently
var batchTasks = new Task[batch.Count()];
for (var i = 0; i < batch.Count(); i )
{
batchTasks[i] = new Task(() => webRequest(batch.ElementAt(i)));
}
foreach (var batchToExec in batchTasks)
{
batchToExec.Start();
}
Task.WaitAll(batchTasks);
}
Но, похоже, возникают некоторые проблемы с учетом того факта, что пакеты должны выполняться в разное время и заполнять результирующую очередь.
Кто-нибудь может помочь?
Комментарии:
1.
Action<int> webRequest = async (id) =>
<== У вас естьasync void
право здесь.2. @TheodorZoulias и?
3. Async void = проблемы, если только он не используется по прямому назначению, то есть для обеспечения возможности асинхронных обработчиков событий. К сожалению, язык C # позволяет очень легко создавать
async void
лямбды по ошибке, поэтому мы должны быть осторожны каждый раз, когда используемasync
ключевое слово с лямбдой.4. @TheodorZoulias Хорошо, спасибо. Я займусь этим вопросом
Ответ №1:
Похоже Tasks
, что вы создаете запуск другой задачи и не ждете их завершения.
После await httpClient.GetAsync
достижения ваша общая задача будет завершена, если она не ожидает результата webRequest
действия.
Измените webRequest
тип на Func<int, Task>
и ждите их.
Ваш пример кода будет выглядеть так:
//Pretend that HttpClient is already configured
//Concurrent queue to collect results from webservice, result order it's not important
var resultsCollection = new ConcurrentQueue<string>();
string inventedWebCall = @"http://fantasywebapi.com/searchById?";
//list of ids
var ids = Enumerable.Range(1, 25);
//Action that calls webrequest and enqueues result in thread safe queue
Func<int, Task> webRequest = async (id) => {
var webCall = $"{inventedWebCall}{id}";
var webResult = await httpClient.GetAsync(new Uri(webCall));
if (webResult.IsSuccessStatusCode)
{
resultsCollection.Enqueue(await webResult.Content.ReadAsStringAsync())
}
};
//Gets a list of lists divided by 10 (10, 10, 5)
var batches = ids.Batch(10, batch => batch);
foreach (var batch in batches)
{
//Contains the tasks to await concurrently
var batchTasks = new Task[batch.Count()];
for (var i = 0; i < batch.Count(); i )
{
batchTasks[i] = webRequest(batch.ElementAt(i));
}
Task.WaitAll(batchTasks);
}