3

如果我们有一个像 ImmutableList() 这样的不可变对象。在多线程环境中使用此对象的首选方法是什么?

例如

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 是否足以实现所需的行为?还是最好使用其他线程函数?

4

3 回答 3

6

“首选”是上下文相关的。最简单的方法是使用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>etc. 或者只是 aList<T>lock.

于 2013-05-13T11:29:19.033 回答
2

一种简单有效的方法是使用ImmutableInterlocked.Update。你向它传递一个方法来执行添加。objList它调用您的 add 方法,然后如果列表在添加期间没有更改,则自动将新值分配给。如果列表发生更改,Update请再次调用您的 add 方法以重试。它会不断重试,直到能够写入更改。

ImmutableInterlocked.Update(ref objList, l => l.Add(o));

如果你有很多写争用,这样你会在重试上花费太多时间,那么objList最好在一些稳定的对象(不是)上使用锁。

于 2020-10-29T14:53:59.940 回答
0

volatile在这种情况下不会帮助您 - 它不会在读取objList、调用Add()和分配之间创建锁objList。您应该改用锁定机制。volatile只是防止操作重新分配。

在您的情况下,您每次添加对象时都会创建一个新列表 - 通常更好的选择是在本地线程变量中创建列表(这样它不会受到多线程问题的影响)并且一旦列表是创建,将其标记为不可变或为其创建不可变包装器。这样,您将获得更好的性能和内存使用率。

于 2013-05-13T11:26:34.910 回答