更新对不可变对象的引用的首选方法是什么?
本文关键字:方法 是什么 引用 不可变 对象 更新 | 更新日期: 2023-09-27 18:13:13
如果我们有一个不可变的对象,如immutableelist()。在多线程环境中使用这个对象的首选方法是什么?
如
public class MutableListOfObjects()
{
private volatile ImmutableList objList;
public class MutableListOfObjects()
{
objList = new ImmutableList();
}
void Add(object o)
{
// Adding a new object to a list will create a new list to ensure immutability of lists.
// Is declaring the object as volatile enough or do we want to
// use other threading concepts?
objList = objList.Add(o);
}
// Will objList always use that lest version of the list
bool Exist(object o)
{
return objList.Exist(o);
}
}
声明引用volatile是否足以实现期望的行为?还是使用其他线程函数更好?
"Preferred"是上下文。最简单的方法是使用lock
,在大多数情况下,这将非常有效地完成工作。如果你有充分的理由认为lock
是一个问题,那么Interlocked
是有用的:
bool retry;
do {
var snapshot = objList;
var combined = snapshot.Add(o);
retry = Interlocked.CompareExchange(ref objList, combined, snapshot)
!= snapshot;
} while(retry);
这基本上适用于一个乐观的但检查路径:大多数情况下,它只会通过一次。偶尔有人会在我们不注意的时候改变objList
的值——没关系,我们只是再试一次。
ConcurrentBag<T>
等。或者只是一个List<T>
和一个lock
。
一个简单而有效的方法是使用ImmutableInterlocked.Update。你传递给它一个方法来执行添加。它调用你的add方法,然后如果列表在添加过程中没有改变,它会自动将新值分配给objList
。如果列表改变了,Update
会再次调用你的add方法来重试。它会不断重试,直到能够写入更改。
ImmutableInterlocked.Update(ref objList, l => l.Add(o));
如果你有很多写争用,这样你就会花费太多的时间在重试上,那么在一些稳定的对象(不是objList
)上使用锁是更好的。
volatile
在这种情况下不会帮助您-它不会在读取objList
,调用Add()
和分配objList
之间创建锁。您应该使用锁定机制。volatile
只是防止操作重新分配。
在您的情况下,每次添加对象时都要创建一个新列表-通常更好的替代方法是在本地线程变量中创建列表(这样它就不会受到多线程问题的影响),并且一旦创建列表,将其标记为不可变或为其创建不可变包装器。这样,您将获得更好的性能和内存使用。