如何在C#</string>中正确锁定List <string> getter

时间:2011-05-08 19:55:05

标签: c# list locking

我想知道如何正确锁定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
    }
}

}

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

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

6 个答案:

答案 0 :(得分:7)

不,它没有。

所有确保的是,当您从属性中返回列表时,列表不会被修改 由于现场访问已经保证是原子的,因此它是完全没用的锁。

你需要锁定整个foreach循环。


请注意,使用ReaderWriterLockSlim可以获得更好的性能,使用ConcurrentBag<T>而不使用任何锁定可以获得更好的性能。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

使用副本可以安全:

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

不,您的读者仅在属性访问期间锁定列表。如果这是你想要的,你需要围绕整个foreach循环锁定列表。

答案 5 :(得分:0)

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

您不应该有一个返回对列表的引用的属性,您应该拥有符合列表所需要的方法:

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;
    }
  }

}