【问题标题】:Thread safe WebApi put requests线程安全的 WebApi put 请求
【发布时间】: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


【解决方案1】:

您正在寻找Concurrency Tokens。工资表中的每一行都有一个。当用户加载工资单的编辑界面时,并发令牌将被发送到客户端。客户端将在更新工资单的请求中包含并发令牌。只有并发令牌未更改,更新才会成功 - 这意味着数据自用户获取数据以开始编辑后未更改。

实体框架也在内部使用并发令牌,因此它不会保存来自过时实体的更改(数据自加载以来已更改)。

当前的 IsLocked 解决方案存在一些缺陷。如果同时收到两个 API 请求,则两者都可以读取工资单数据并看到它没有被锁定。然后,两个请求都会锁定该行,进行竞争性更改,然后释放锁定,而不会意识到有同时进行的编辑。

【讨论】:

  • 如何在API端实现不使用客户端发送此令牌?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-04
  • 2014-07-06
  • 2021-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多