【问题标题】:Dependency Injected Service Life and Concurrency Issues依赖注入服务寿命和并发问题
【发布时间】:2021-10-05 08:27:53
【问题描述】:

我有一个无法找到确切答案/理解的场景。考虑有一个IAccountService,它有方法GenerateUniqueAccountNumber。有一个几分钟的窗口(假设是 10 分钟),很多用户(可能在 6K - 8K 左右) 将调用此方法来生成他们的帐号.此方法将根据计数器值返回一个新数字,该数字在 db 中维护。意思是,它将:

  1. 从 db 获取最新发布的帐号,作为计数器维护。 (例如 1000)
  2. 加一。 (例如 1001)
  3. 将号码分配给呼叫者的帐户并保存。 (一些业务逻辑)
  4. 将计数器值(最新发布的号码)设置为 1001。以便下一个调用者获得 1002。

我的问题是,如何保证帐号的唯一性?我预计会有一些并发调用。那么创建一个单独的服务IAccountNumberManager 并在其中移动GenerateUniqueAccountNumber 方法是个好主意。然后将此服务注册为AddSingletonlifetime?在这种情况下,将其注册为Singleton 会有什么不同吗?

我应该在IAccountService 内的相关代码中应用lock,以保证并发请求得到正确/有序的帐号?

我应该两者都做?意思是,创建IAccountNumberManager,注册为AddSingleton生命周期,也申请lock

我们将不胜感激。

【问题讨论】:

  • 最好保证数据库端的唯一性。也就是说,数据库应该为您提供一个值并增加它,而不是您的代码。
  • 自动增量会很有用
  • @Evk 不,因为这只是一个例子。实际上,我要做的不仅仅是增加数字。这就是为什么我明确指定了我的问题。
  • 除非您要求没有间隙(例如帐号 1 后跟 3,中间没有 2) - 您可以同时从数据库中获取和增加值,然后用它执行业务逻辑。

标签: c# asynchronous .net-core dependency-injection concurrency


【解决方案1】:

假设您只会运行一个服务实例,那么使用锁就可以了。

不幸的是,在这里使用单例对您没有帮助,因为您仍然可以同时在该方法中拥有两个线程,并且您可以获得经典的读-修改-写竞争条件:

Thread 1: Read counter (eg. 1000)
Thread 2: Read counter (eg. 1000)
Thread 1: Increment value
Thread 2: Increment Value
Thread 1: Write Value (eg. 1001)
Thread 2: Write Value (eg. 1001)

另一种方法(仍然假设您有一个实例)是使用Interlocked.Increment 来避免需要锁定。这是一个原子增量操作,它使用处理器的特殊功能来确保它不会遇到上述竞争条件。

但是,如果您必须扩展此服务以同时运行 1 个以上的实例,则必须使用不同的解决方案。这里可能的解决方案包括:

  1. 正如 @Evk 所建议的,将 ID 分配保留在数据库中,它会为您处理所有并发问题
  2. 为每个实例分配一组帐户 ID,例如实例 1 预留 1000-1999,实例 2 预留 2000-2999,然后它们可以独立于它们的预留进行分配。这样做的缺点是 ID 不是连续的,但意味着您将分配保留在您的服务中。
  3. 考虑替代方法,而不是使用连续整数,例如使用 GUID 或命名空间 ID,其中 ID 包括分配 ID 的实例的 ID

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-21
    • 2020-09-09
    • 2014-05-27
    • 2021-09-11
    相关资源
    最近更新 更多