c# - 包含存储过程的异步方法
问题描述
在我的控制器中,这些[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();
避免并发的正确方法吗?它有什么作用吗?有没有更好的方法来实现这一点,或者这是否足够好?
解决方案
异步与并发无关。事实上,如果有的话,它会增加并发问题,因为异步操作没有定义的顺序,因此是“异步的”。但是,在像 Web 应用程序这样的多线程环境中,并发是一个问题,无论是同步还是异步,因为大约 1000 个线程中的每一个都可以同时执行相同的工作。
处理并发只有两种真正的方法:锁和并发令牌。并发令牌是最好的方法,但它们仅适用于现有的事物(而不是插入)。基本上,您有一列存储并发令牌,并且每次对该行进行操作时,都会更新令牌。然后,在您实际执行操作之前,您检查令牌是否与您首先获得该行时相同(意味着没有任何变化)。如果是这样,那么您可以继续操作。如果不是,那么其他东西已经对其进行了修改,因此继续进行下去不再安全。此检查是通过 where 子句完成的。例如:
update Foos set Bar = 'Baz' where Id = 1 and Version = {token}
如果 in 中的标记Version
不再相同,那么显然没有行将匹配并且没有任何更新。然后,您可以使用返回的操作计数(即零)来确定存在并发故障并进行恢复。
这不适用于插入,因为没有什么可以确定是否发生更改。(您不能在插入中使用 where 子句。)对于这种情况,您别无选择,只能使用锁来防止在执行操作时发生其他任何事情。但是,锁绝对会扼杀您的吞吐量,因为它本质上会强制后续请求排队,并且一次只能处理一个。因此,当并发成为问题时,您应该完全避免这些类型的操作。(不相互冲突的插入是可以的。)