I noticed that in questions discussing immutable collections (e.g. What is the preferred method of updating a reference to an immutable object?)
it was advised to use Interlocked
(or better ImmutableInterlocked
). The reason for this seems to be for CAS, since in most cases an immutable collection is updated based off of its previous value. However, suppose I have some class wrapping an immutable collection which I expect to be accessed concurrently and where new values do not depend on old values, would using Interlocked
still be preferred? In other words, is there any reason for me to prefer this:
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);
}
}
over this:
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;
}
}
No, there is no reason. Marking the values
field as volatile
is enough to ensure that all threads will see the latest value stored in this field.
It should be noted though that the power of the immutable collections, and the ImmutableList<T>
in particular, lies on their ability to reuse most of their internal state every time they are updated. Most of them are implemented as binary trees, and updating them results in replacing a few nodes in the tree. If you are going to update the whole collection each time, then you get all the disadvantages of this approach (overhead, memory size), and none of its advantages (memory reusability).
The exception to this rule is the 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>
.