高容量POST控制器asp核心1.1

时间:2017-11-05 12:52:01

标签: c# .net asp.net-mvc asp.net-core task-parallel-library

我需要一个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我的{{1}}指数达到5时甚至更难。 问题是什么?

2 个答案:

答案 0 :(得分:0)

你可以尝试一下吗?

for (int i = 0; i < lv.Count; i++)
    {
        var item = lv[i];
        tasks[i] = Task.Run(() => _ADB.SaveLogView(item);                    
    }

答案 1 :(得分:0)

目前还不清楚你最终会在这里做什么,但如果你的目标是高性能,那么这是实现这一目标的绝对错误方法。每次调用时,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一次,这将尝试一次性保存所有项目,包含在事务中,所以如果任何失败,一切都会回滚。

如果您正在使用实体框架或其他支持交易的系统,您应该使用该功能。如果您正在进行某种手动数据库工作,那么您应该自己进行交易。