高容量 POST 控制器 asp 核心 1.1
High volume POST controller 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。
有什么问题吗?
你能试试这个吗?
for (int i = 0; i < lv.Count; i++)
{
var item = lv[i];
tasks[i] = Task.Run(() => _ADB.SaveLogView(item);
}
不清楚您最终要在这里做什么,但如果您的目标是高性能,那么这绝对是错误的实现方式。 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 或其他一些支持后台事务的系统,您应该使用该功能。如果您正在进行某种手动数据库工作,那么您应该滚动自己的事务。
我需要一个 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。
有什么问题吗?
你能试试这个吗?
for (int i = 0; i < lv.Count; i++)
{
var item = lv[i];
tasks[i] = Task.Run(() => _ADB.SaveLogView(item);
}
不清楚您最终要在这里做什么,但如果您的目标是高性能,那么这绝对是错误的实现方式。 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 或其他一些支持后台事务的系统,您应该使用该功能。如果您正在进行某种手动数据库工作,那么您应该滚动自己的事务。