首页 > 解决方案 > 包含存储过程的异步方法

问题描述

在我的控制器中,这些[POST]方法通常从数据库中调用存储过程。我想知道我是否在做正确的事情以避免并发问题。以下面的方法为例:

    [HttpPost]
    public async Task<IActionResult> Create(Phase phase)
    {
        int releaseId = (int)TempData["id"];
        string connectionString = Configuration["ConnectionStrings:DefaultConnection"];
        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            string sql = "CreatePhase";

            using (SqlCommand command = new SqlCommand(sql, connection))
            {
                command.CommandType = CommandType.StoredProcedure;

                // adding parameters
                SqlParameter parameter = new SqlParameter
                {
                    ParameterName = "@Name",
                    Value = phase.Name,
                    SqlDbType = SqlDbType.VarChar,
                    Size = 50
                };
                command.Parameters.Add(parameter);

                parameter = new SqlParameter
                {
                    ParameterName = "@ReleaseId",
                    Value = releaseId,
                    SqlDbType = SqlDbType.Int
                };
                command.Parameters.Add(parameter);

                connection.Open();
                await command.ExecuteNonQueryAsync();
                connection.Close();
            }
        }

        return RedirectToAction("Index", "Phase", new { id = releaseId });
    }

await command.ExecuteNonQueryAsync();避免并发的正确方法吗?它有什么作用吗?有没有更好的方法来实现这一点,或者这是否足够好?

标签: c#asp.net-coreconcurrency

解决方案


异步与并发无关。事实上,如果有的话,它会增加并发问题,因为异步操作没有定义的顺序,因此是“异步的”。但是,在像 Web 应用程序这样的多线程环境中,并发是一个问题,无论是同步还是异步,因为大约 1000 个线程中的每一个都可以同时执行相同的工作。

处理并发只有两种真正的方法:锁和并发令牌。并发令牌是最好的方法,但它们仅适用于现有的事物(而不是插入)。基本上,您有一列存储并发令牌,并且每次对该行进行操作时,都会更新令牌。然后,在您实际执行操作之前,您检查令牌是否与您首先获得该行时相同(意味着没有任何变化)。如果是这样,那么您可以继续操作。如果不是,那么其他东西已经对其进行了修改,因此继续进行下去不再安全。此检查是通过 where 子句完成的。例如:

update Foos set Bar = 'Baz' where Id = 1 and Version = {token}

如果 in 中的标记Version不再相同,那么显然没有行将匹配并且没有任何更新。然后,您可以使用返回的操作计数(即零)来确定存在并发故障并进行恢复。

这不适用于插入,因为没有什么可以确定是否发生更改。(您不能在插入中使用 where 子句。)对于这种情况,您别无选择,只能使用锁来防止在执行操作时发生其他任何事情。但是,锁绝对会扼杀您的吞吐量,因为它本质上会强制后续请求排队,并且一次只能处理一个。因此,当并发​​成为问题时,您应该完全避免这些类型的操作。(不相互冲突的插入是可以的。)


推荐阅读