如何更新缓存列表,而其他线程保留旧的缓存列表

问题描述 投票:0回答:1

我的应用程序中有一个缓存的用户列表。几个管理页面需要所有用户的列表(总数足够小,这不是问题)。

我采取的方法是:

  1. 当请求缓存列表时,该列表不会改变。
  2. 当用户为CrUD时,会调用将其添加到缓存中。
  3. 进行该调用时,现有列表将被复制到新列表中,并且 CrUD 用户将从列表中添加/更新/删除。该列表就是新的缓存列表。

好主意。但我刚刚编写了一些执行此操作的单元测试,但它不起作用。因为有 2 个线程正在重建列表,而第二个写入列表的线程获胜。我可以在列表的重建周围加上

lock (this)
,效果很好。

但这是一个糟糕的方法。非常适合识别问题,但不适合生产。

在正常使用中,这种返回任何列表都是不可变的方法非常有效。如果没有

lock
我怎样才能完成这个任务?

在 C# 中,这是一个与语言无关的问题,尽管可能存在一些 C# 类等,这使得这是一个简单的解决方案。

c# multithreading thread-safety
1个回答
0
投票

如果没有

lock
我怎样才能完成这个任务?

你不能。至少如果您想坚持使用包含可变

List<T>
对象的
User
,至少不需要。
List<T>
集合不是线程安全的。即使是这样,在不同步的情况下改变它包含的
User
元素也会导致错误/未定义的行为。

我可以在列表的重建周围加上

lock (this)
,效果很好。但这是一个糟糕的方法。

不一定。如果您仔细使用

lock
语句,这意味着您仅保护与共享状态(列表和用户)的交互,而不是在共享之前重建新列表,则 contention 是可能很小,而且性能相当不错。

当然还有其他可用的选项,不涉及共享

List<T>
,例如:

  1. ConcurrentDictionary<K,V>
    已更新为
    GetOrAdd
    /
    AddOrUpdate
  2. ImmutableList<T>
    已更新为
    ImmutableInterlocked.Update

这两种低锁替代方案都要求

User
类型应该是不可变的。它们不仅会表现得更好(很可能),而且也会更方便。

© www.soinside.com 2019 - 2024. All rights reserved.