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