【发布时间】:2022-01-23 11:43:38
【问题描述】:
我有一个 webapi,我想让这个控制器线程中的逻辑安全。
我希望用户只能在最后一个更新并且不应该同时更新两个更新时更新工资单。
正如您在代码中看到的那样,我在 Payroll 实体中添加了一个名为 IsLock 的列作为 boolean 并尝试以这种方式处理多个更新请求,但它不是线程安全的。
我怎样才能做到thread-safe?
[HttpPut("{year}/{month}")]
public async Task<NoContentResult> Approve([FromRoute] int year, [FromRoute] int month)
{
var payroll = _dataContext.Payrolls
.SingleOrDefaultAsync(p =>
p.Month == month && p.Year == year);
if (payroll.IsLock)
{
throw new ValidationException(
$"The payroll {payroll.Id} is locked.");
}
try
{
payroll.IsLock = true;
_dataContext.Payrolls.Update(payroll);
await _dataContext.SaveChangesAsync(cancellationToken);
payroll.Status = PayrollStatus.Approved;
_dataContext.Payrolls.Update(payroll);
await _dataContext.SaveChangesAsync(cancellationToken);
payroll.IsLock = false;
_dataContext.Payrolls.Update(payroll);
await _dataContext.SaveChangesAsync(cancellationToken);
return NoContent();
}
catch (Exception)
{
payroll.IsLock = false;
_dataContext.Payrolls.Update(payroll);
await _dataContext.SaveChangesAsync(cancellationToken);
throw;
}
}
【问题讨论】:
-
这有用吗? C# Lock and Async Method
-
@TheodorZoulias 我不确定为 webapi 端点添加锁或信号量是否是个好主意。我的意思是这是最好的做法吗?处理这种情况的最佳方法是什么?
-
注意:一旦您向外扩展并且超过 1 个实例正在处理请求,使其线程安全将不起作用。然后你需要一个分布式锁或其他类似队列的东西。
-
您可以使用事务或 EF Core 的concurrency tokens。发出 3 个请求而不是 1 个请求并没有多大意义 TBH。
-
添加
SemaphoreSlim(1, 1)是防止在单个进程上同时执行包含await的代码块的最佳方法。我不确定 WebAPI 是否满足单进程要求。如果不是,您将需要一个命名信号量(它是跨进程的),如 here 所示。
标签: c# multithreading concurrency thread-safety webapi