Как правильно заблокировать список getter в C#
мне интересно, Как правильно заблокировать геттер типа List<String>
. У меня один статический класс, который выглядит примерно так:
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 ответов
нет, это не так.
все, что гарантирует, что список не изменяется по мере его возврата из свойства.
Поскольку доступ к полю уже гарантирован атомарным, это совершенно бесполезный замок.
вам нужно поставить замок вокруг всего foreach
петли.
обратите внимание, что вы можете достичь лучшей производительности с помощью ReaderWriterLockSlim и даже лучшей производительности с помощью ConcurrentBag<T>
без каких-либо блокировок.
нет, это не так. В частности, первый показанный замок защищает только на время получение ссылки на список. После этого другие потоки могут конкурировать, пока это делает вызывающий... Ну, что бы они ни делали после доступа my_list
.
вы будете в безопасности с копией:
public static List<String> my_list
{
get {
lock(locker) {
return my_list.ToList();
}
}
}
Это зависит от вашего req, если это приемлемо.
возможно, вместо того, чтобы блокировать ваши списки, вы могли бы выставить их как 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;
}
}
}