如何在<String> C# 中正确锁定列表获取器



我想知道如何正确锁定 List<String> 类型的 getter .我有一个静态类,看起来像这样:

class FirstClass {
static private locker = new object();   
static private List<String> _my_list;
public static List<String> my_list {
    get {
        lock(locker) {
            return my_list;
        }
    }
}
private static void thread_func() {
    // do something periodicaly with the list
    // for example:
    lock(locker){
        _my_list.Add();
        _my_list.RemoveAt();
        ...
    }
}

}

然后,我有另一个看起来像这样的类:

class SecondClass {
private void thread_func() {
    foreach(string s in FirstClass.my_list) {
        // read every item in the list
    }
}

}

因此,第一类有一个公共列表,第二类使用。第一个类在一个线程中定期更新列表,第二个类在第二个线程上以随机间隔读取列表。

此锁定机制是否确保在第二个类读取列表时不会修改列表,反之亦然?

不,它没有。

所有确保的是,当您从属性返回列表时,不会修改列表。
由于字段访问已经保证是原子的,因此它是一个完全无用的锁。

您需要在整个foreach循环周围放置一个锁。


请注意,通过使用 ReaderWriterLockSlim 可以获得更好的性能,通过使用没有任何锁的ConcurrentBag<T>可以获得更好的性能。

不,它没有。特别是,显示的第一个锁仅在获取列表引用的持续时间内提供保护。之后,其他线程可以竞争,而调用者可以竞争...好吧,无论他们在访问my_list后做什么.

使用副本会很安全:

public static List<String> my_list 
{
    get {
        lock(locker) {
            return my_list.ToList();
        }
    }
}

这取决于您的要求是否可以接受。

也许与其

锁定列表,不如将它们公开为 ReadOnlyCollections,并提供一种添加到处理逻辑的列表的方法。

否,您的读取器仅在属性访问期间锁定列表。如果需要,您需要将列表锁定在整个 foreach 循环周围。

不,您拥有的锁定是完全无用的。它只会保护对包含列表的变量的访问,但是一旦您获得了该引用的副本,对列表的访问就完全不受保护。

你不应该有一个返回对列表的引用的属性,你应该有一个对列表执行所需操作的方法:

class FirstClass {
  static private locker = new object();   
  static private List<String> _my_list;
  public static void Add(string item) {
    lock(locker) {
      my_list.Add(item);
    }
  }
  public static void RemoveAt(int index) {
    lock(locker) {
      my_list.RenoveAt(index);
    }
  }
  public static IEnumerable<string> GetEnumerable() {
    lock(locker) {
      List<string> copy = new List<string>(my_list);
      return copy;
    }
  }
}

最新更新