我想知道如何正确锁定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
}
}
}
因此,第一类有一个第二类使用的公共列表。第一类在一个线程中定期更新列表,第二类在第二个线程上以随机间隔读取列表。
这个锁定机制是否确保第一个类在第二个类读取时不会修改列表,反之亦然?
答案 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;
}
}
}