我的应用程序中有一个缓存的用户列表。几个管理页面需要所有用户的列表(总数足够小,这不是问题)。
我采取的方法是:
- 当请求缓存列表时,该列表不会改变。
- 当用户是 CrUD 时,会调用将其添加到缓存中。
- 当进行该调用时,现有列表将被复制到新列表中,并且 CrUD 用户将被添加/更新/从列表中删除。该列表就是新的缓存列表。
好想法。但我刚刚编写了一些执行此操作的单元测试,但它不起作用。因为有 2 个线程正在重建列表,而第二个写入列表的线程获胜。我可以重建lock (this)
列表,效果很好。
但这是一个糟糕的方法。非常适合识别问题,但不适用于生产。
在正常使用中,这种返回任何列表都是不可变的方法效果很好。如果没有 ,我怎样才能完成这个任务lock
?
在 C# 中,这是一个与语言无关的问题,尽管可能有一些 C# 类等使其成为一个简单的解决方案。
你不能。至少如果您想坚持使用
List<T>
包含可变User
对象的 a ,则至少不会。该List<T>
集合不是线程安全的。即使是这样,User
在没有同步的情况下改变它包含的元素也会导致错误/未定义的行为。不必要。如果您仔细使用该
lock
语句,这意味着您仅保护与共享状态(列表和用户)的交互,而不是在新列表共享之前重建新列表,则争用可能会很小,并且性能会提高相当不错。当然,还有其他可用的选项,不涉及共享
List<T>
. 例如:ConcurrentDictionary<K,V>
更新为GetOrAdd
/AddOrUpdate
。ImmutableList<T>
更新为ImmutableInterlocked.Update
.这两种低锁替代方案都要求
User
类型必须是不可变的。它们不仅会表现得更好(很可能),而且也会更方便。ConcurrentDictionary<K,V>
使用 a和record class
as的示例User
:在线演示。