Большой объем POST контроллер Asp Core 1.1
Мне нужно, чтобы POST-контроллер обрабатывал много трафика, я сделал что-то подобное
[HttpPost]
public async Task<IActionResult> Post([FromBody] List<VdoPlayInfo> lv)
{
var seesionID = HttpContext.Session.GetString("sessionId");
var vdoID = HttpContext.Session.GetInt32("videoid");
var tasks = new Task[lv.Count];
for (int i = 0; i < lv.Count; i++)
{
tasks[i] = Task.Run(() => _ADB.SaveLogView(lv[i]);
}
await Task.WhenAll(tasks);
return Ok();
}
после нескольких секунд работы я получаю эту ошибку
System.ArgumentOutOfRangeException: 'Index was out of range.
Must be non-negative and less than the size of the collection.'
даже жесткая lv.Count = 2
Я получаю i
индекс достигает 5. В чем проблема?
2 ответа
Непонятно, что вы в конечном итоге пытаетесь сделать здесь, но если ваша цель - высокая производительность, это абсолютно неверный способ достичь этого. Task.Run
будет вытягивать новый поток из пула каждый раз, когда вы вызываете его, так что с двумя элементами в вашем списке ваше действие теперь потребляет три потока (один для запроса и один для каждого элемента списка). Очевидно, что по мере масштабирования элементов списка увеличивается и использование потоков, поэтому вы резко сокращаете потенциальную пропускную способность вашего сервера и, возможно, в конечном итоге будете нуждаться в потоке.
Не понятно что _ADB.SaveLogView
делает, но если он выполняет синхронную работу, вы должны понимать, что с помощью Task.Run
не делает это асинхронным Синхронизация по-прежнему синхронизируется, вы просто блокируете другой поток вместо потока запроса. Однако, поскольку вы ожидаете завершения задачи, поток запросов по- прежнему блокируется, поэтому вы на самом деле не достигаете ничего, кроме как просто тратить кучу потоков, которые могут обрабатывать другие запросы.
Если _ADB.SaveLogView
является асинхронным (в этом случае вы должны назвать его _ADB.SaveLogViewAsync
чтобы избежать путаницы), тогда вы можете упростить свой код (и не тратить потоки), просто выполнив:
var tasks = new Task[lv.Count];
for (int i = 0; i < lv.Count; i++)
{
tasks[i] = _ADB.SaveLogViewAsync(lv[i]);
}
Поскольку он уже должен возвращать Task
в этом случае нет необходимости оборачивать его в другой Task
,
Тем не менее, в идеале, вы должны на самом деле обрабатывать это транзакционно. Если при сохранении одной из этих сущностей возникает проблема, остальные все равно сохраняются. Это может вызвать проблемы, если вам нужно повторно отправить, чтобы сохранить объект или объекты, которые потерпели неудачу. Опять же, не ясно, что делает этот метод, но с чем-то вроде Entity Framework вы просто добавляете каждую новую сущность в DbSet
(который помечает их как созданные в отслеживании изменений), а затем вызывает SaveChangesAsync
только один раз, который попытался бы сохранить все элементы за один раз, завернутые в транзакцию, так что в случае неудачи все откатывается.
Если вы используете Entity Framework или какую-либо другую систему, которая поддерживает транзакции внутри системы, вы должны использовать эту функцию. Если вы выполняете какую-то ручную работу с базой данных, то вам нужно выполнить свои собственные транзакции.
Не могли бы вы попробовать это?
for (int i = 0; i < lv.Count; i++)
{
var item = lv[i];
tasks[i] = Task.Run(() => _ADB.SaveLogView(item);
}