对于更新不依赖于先前值的不可变集合,是否有任何理由更喜欢 Interlocked 而不是 volatile?

Is there any reason to prefer Interlocked over volatile for an Immutable Collection where updates do not depend on previous values?

我注意到在讨论不可变集合的问题中(例如 What is the preferred method of updating a reference to an immutable object?) 建议使用 Interlocked(或更好的 ImmutableInterlocked)。这样做的原因似乎是 CAS,因为在大多数情况下,不可变集合是根据其先前的值进行更新的。但是,假设我有一些 class 包装一个不可变的集合,我希望它能被同时访问,并且新值不依赖于旧值,那么使用 Interlocked 仍然是首选吗?换句话说,我有什么理由更喜欢这个:

public class Data {
   private ImmutableList<string> values;

   public ImmutableList<string> Read() {
       return Interlocked.CompareExchange(ref values, null, null);
   }

   public void Write(IEnumerable<string> update) {
      //some arbitrary transformation that doesn't use values at all
      var newValues = update.Select(x => x + "-update").ToImmutableList(); 

      Interlocked.Exchange(ref values, newValues);
   }
}

关于这个:

public class Data {
   private volatile ImmutableList<string> values;

   public ImmutableList<string> Read() {
       return values;
   }

   public void Write(IEnumerable<string> update) {
      //some arbitrary transformation that doesn't use values at all
      var newValues = update.Select(x => x + "-update").ToImmutableList();

      values = newValues;
   }

}

不,没有理由。将 values 字段标记为 volatile 足以确保所有线程都将看到存储在该字段中的最新值。

不过应该注意的是,不可变集合(尤其是 ImmutableList<T>)的强大之处在于它们每次更新时都能够重用大部分内部状态。它们中的大多数被实现为二叉树,更新它们会导致替换树中的几个节点。如果你每次都更新整个集合,那么你会得到这种方法的所有缺点(开销,内存大小),以及它的优点(内存可重用性)none。

此规则的例外是 ImmutableArray<T> struct, which is implemented internally as a simple array. It offers no reusability, but it's very performant, so it seems ideal for your case. To overcome the fact that it's a value type, you can wrap it in a Tuple<T>, or store it in a field of type IImmutableList<T>