Как правильно заблокировать список 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;
    }
  }

}