【问题标题】:C# Immutable HashSet lockingC# 不可变哈希集锁定
【发布时间】:2018-07-20 03:27:21
【问题描述】:

假设一个服务器存储了一个连接数据的 ImmutableHashSet

ImmutableHashSet<ConnectionData> connections = new ...

然后我有各种调用添加/删除/读取这个,即:

OnConnected(connectionData) => connections = connections.Add(connectionData);
OnDisconnected(connectionData) => connections = connections.Remove(connectionData);

我的问题是,在上述仅对 HashSet 执行单个操作(添加/删除)的调用中,我应该锁定连接吗?还是 ImmutableHashSet 操作线程安全?

【问题讨论】:

  • 你对connections变量做了两个操作。
  • ImmutableHashSet 操作线程安全吗?阅读文档 - 但这不是全部
  • 您应该阅读 @EricLippert 2007 年末关于不变性的博客系列,从这里开始:blogs.msdn.microsoft.com/ericlippert/2007/11/13/…。如果集合是不可变的,那么您引用的集合始终具有内部完整性,并且,它永远不会改变。但是,另一个线程上的参与者所引用的不一定是同一个。

标签: c# c#-7.0


【解决方案1】:

您将值直接分配回集合的模式不是线程安全的。但是,在使用不可变集合时,您通常不想lock。这些集合的主要功能之一是无锁操作,这通常显着提高性能。相反,请使用 ImmutableInterlocked 类:

ImmutableHashSet<ConnectionData> connections;

ImmutableInterlocked.Update(ref connections,
                            (collection, item) => collection.Add(item),
                            connectionData);

ImmutableInterlocked.Update(ref connections,
                            (collection, item) => collection.Remove(item),
                            connectionData);

【讨论】:

  • “你通常不想在使用不可变集合时加锁”——为什么?
  • @kofifus Lock-free 更便宜,因为当争用较少时,它平均会做更少的工作。在高争用期间,无锁仍然会更快,但速度会降低——而且它往往会分配更多。幸运的是,这些分配都将在 gen-0 GC 中,这非常便宜。也就是说,如果您有高争用,那么无论锁定如何,不可变集合都不适合:它们不是为高写入争用而设计的。在这种情况下,您可能想使用类似ConcurrentDictionary 的东西,它支持实际的并发使用。
  • 有趣并感谢您的解释.. 是否有任何关于 ImmutableInterlocked 如何工作的好的解释/讨论? (似乎微软文档对于每种方法只有 1-2 行)
  • @kofifus 您可以通过下载 dotPeek 并反编译 ImmutableInterlocked.Update 来验证该方法自己的作用。总之,它会循环调用Interlocked.CompareExchange,直到成功。
  • 所以如果我理解正确,从更新返回的布尔值在这种情况下将代表 hashset 方法的结果,如果返回 true,则添加/删除值,如果返回 false这意味着在“旧”和新集合之间没有发现任何区别(例如,在我们有机会之前添加/删除了另一个线程,或者该值已经存在/从未存在)......所以......出于意图和目的究竟是什么您会期望使用锁的 HashSet 上的 Add/Remove 方法,是吗?
【解决方案2】:

AddRemove 方法调用返回一个全新的 ImmutableHashSet。因此,虽然类型是 thread-safe,但您将需要使用锁定 - 来处理同时发生对 AddRemove 的多个调用的情况。 否则,如果两个Add 调用同时发生(例如),您就会遇到典型的竞争条件。

因此,您将需要使用锁定对象:

private object lockObject = new lockObject();

然后在分配给connections 的每个代码块周围使用lock(lockObject)(例如,您的AddRemove 调用)。

另请参阅上述 cmets 中 @Flydog57 提供的精彩链接。

【讨论】:

    猜你喜欢
    • 2020-06-07
    • 1970-01-01
    • 2021-09-05
    • 1970-01-01
    • 2019-12-20
    • 2016-08-12
    • 2018-04-04
    • 2013-08-07
    • 1970-01-01
    相关资源
    最近更新 更多