对于更新不依赖于先前值的不可变集合,是不是有任何理由更喜欢 Interlocked 而不是 volatile?
Posted
技术标签:
【中文标题】对于更新不依赖于先前值的不可变集合,是不是有任何理由更喜欢 Interlocked 而不是 volatile?【英文标题】:Is there any reason to prefer Interlocked over volatile for an Immutable Collection where updates do not depend on previous values?对于更新不依赖于先前值的不可变集合,是否有任何理由更喜欢 Interlocked 而不是 volatile? 【发布时间】:2021-06-08 02:07:32 【问题描述】:我注意到在讨论不可变集合的问题中(例如What is the preferred method of updating a reference to an immutable object?)
建议使用Interlocked
(或更好的ImmutableInterlocked
)。原因似乎是 CAS,因为在大多数情况下,不可变集合会根据其先前的值进行更新。但是,假设我有一些包装了一个不可变集合的类,我希望它可以同时访问,并且新值不依赖于旧值,使用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;
【问题讨论】:
【参考方案1】:不,没有理由。将values
字段标记为volatile
足以确保所有线程都能看到该字段中存储的最新值。
应该注意的是,不可变集合,尤其是ImmutableList<T>
的强大之处在于它们能够在每次更新时重用大部分内部状态。它们中的大多数被实现为二叉树,更新它们会导致替换树中的一些节点。如果您打算每次都更新整个集合,那么您会得到这种方法的所有缺点(开销、内存大小),而没有任何优点(内存可重用性)。
这个规则的例外是ImmutableArray<T>
结构,它在内部被实现为一个简单的数组。它不提供可重用性,但性能非常好,因此它似乎非常适合您的情况。为了克服它是值类型这一事实,您可以将其存储在类型为 IImmutableList<T>
的字段中。
【讨论】:
感谢您的回答!为了跟进您的第二点——我在内部将其实现为易失性非不可变集合(例如常规List
)并让Read()
返回它的只读视图(如@ 987654332@)?
@asdf 实际上你可以,特别是如果你希望成为这个 API 的唯一消费者。但语义不一样。不可变意味着没有人可以改变它,而只读意味着消费者不能改变它,但所有者可能可以。这是一个相关的问题:Why use ImmutableList over ReadOnlyCollection?以上是关于对于更新不依赖于先前值的不可变集合,是不是有任何理由更喜欢 Interlocked 而不是 volatile?的主要内容,如果未能解决你的问题,请参考以下文章