Потокобезопасный доступ к списку <T>свойств - PullRequest
3 голосов
/ 04 января 2012

Мне было интересно, не вызовет ли это утверждение проблемы с синхронизацией:

List<Character> characters = World.CharacterManager.Characters;

'Знаки' - это класс

'CharacterManager.Characters' будет выглядеть примерно так:

public List<Character> Characters
{
    get
    {
        lock (this.objLock) { return this.characters; }
    }
}

не вызовет ли это проблемы с синхронизацией?

Я хочу использовать список, на который ссылаются, для итераций, чтобы найти искомый символ.

Ответы [ 4 ]

9 голосов
/ 04 января 2012

Проблема в том, что вы блокируете во время get, но как только каждый поток имеет ссылку на коллекцию, они могут одновременно действовать против нее.Поскольку члены List<T> не являются поточно-ориентированными, вы можете столкнуться со случайными ошибками и исключениями при итерации, добавлении, удалении и т. Д. Коллекции.

Возможно, вам потребуется вернуть потокобезопасную коллекцию.Не существует 100% совместимой поточно-ориентированной версии, поэтому вам нужно просмотреть System.Collections.Concurrent и найти версию, которую вы можете использовать.

2 голосов
/ 04 января 2012

Замок таким образом бесполезен. Вам нужно будет использовать потокобезопасную коллекцию, такую ​​как предложенная Will, или, если вам не нужен доступ для записи, вы можете предоставить только версию вашего списка только для чтения, например:

public ReadOnlyCollection<Character> Characters {
  get {
    lock (locker) { return this.characters.AsReadOnly(); }
  }
}

Эти коллекции не могут быть изменены, поэтому, если ваш тип Character неизменен, у вас нет проблем с синхронизацией. Если Character является изменяемым, у вас снова будет проблема, но у вас будет эта проблема даже с потокобезопасной коллекцией. Я надеюсь, что вы знаете об этом. Вы также можете выставить свойство, возвращающее IList<Character>, но обычно мне лучше сообщить вызывающей стороне, что объект доступен только для чтения.

Если вам нужен доступ для записи, вы также можете сделать это, предоставив соответствующие методы в области действия CharacterManager и синхронизировав их. Джесси написал хороший пример того, как это сделать.

РЕДАКТИРОВАТЬ: SyncRoot не присутствует на ICollection .

1 голос
/ 04 января 2012

Нужно ли на самом деле вызывать код для добавления и удаления из списка? Если это так, то не считается лучшей практикой . Вот (возможный) способ реализации без этого требования, вместо этого добавление и удаление Character элементов в сам класс CharacterManager:

internal sealed class CharacterManager
{
    private readonly IList<Character> characters = new List<Character>();

    public ReadOnlyCollection<Character> Characters
    {
        get
        {
            lock (this.characters)
            {
                return this.characters.AsReadOnly();
            }
        }
    }

    public void Add(Character character)
    {
        lock (this.characters)
        {
            this.characters.Add(character);
        }
    }

    public void Remove(Character character)
    {
        lock (this.characters)
        {
            this.characters.Remove(character);
        }
    }
}
0 голосов
/ 04 января 2012

Если вы хотите, чтобы вызывающая сторона могла только перечислять список, тогда ваше свойство должно иметь тип IEnumerable. Если это так, то я бы также сделал копию этого списка и вернул бы копию. Если список будет изменен во время его перечисления, он станет недействительным и выдаст исключение. Компромисс в том, что у вызывающего абонента может не быть последней версии списка. Я был бы склонен преобразовать его в метод с именем GetCharactersAsOfNow() вместо свойства, чтобы показать, что вызывающий должен получать обновленный список для каждого вызова.

Однако, если вы планируете разрешить вызывающему абоненту изменять список, то вы должны отметить, что список не является потокобезопасным, и требовать от вызывающего абонента выполнения синхронизации потоков. Учитывая, что вызывающая сторона теперь несет эту ответственность, вам больше не нужна блокировка в получателе вашей собственности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...