Блокировка, чтобы сделать класс потокобезопасным на примере C # или этот класс потокобезопасен? - PullRequest
4 голосов
/ 19 января 2012

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

    public class StringMe 
    {
        protected ArrayList  _stringArrayList = new ArrayList();
        static readonly object _locker = new object();

        public void AddString(string stringToAdd)
        {
            lock (_locker) _stringArrayList.Add(stringToAdd);
        }

        public override string ToString()
        {
            lock (_locker)
            {
return string.Join(",",string[])_stringArrayList.ToArray(Type.GetType("System.String")));
            }
        }
    }

1) Успешно ли я сделал потоки AddString andToString безопасными?

2) В созданном мною методе ToString необходимо ли блокировать его, чтобы сделать его безопасным для потоков?

3) Только методы, которые изменяют данные, должны быть заблокированы, или же операции блокировки чтения и записи должны быть заблокированы, чтобы сделать их потокобезопасными?

Большое вам спасибо за ваше время!

Ответы [ 3 ]

13 голосов
/ 19 января 2012

Нет, вы не сделали эти вызовы поточно-ориентированными - потому что поле _stringArrayList защищено.Подклассы могут делать с ним все, что им нравится, пока вызываются AddString и ToString.

Например (как и в других ответах, утверждается, что ваш код является поточно-ориентированным.)

public class BadStringMe : StringMe
{
    public void FurtleWithList()
    {
        while (true)
        {
            _stringArrayList.Add("Eek!");
            _stringArrayList.Clear();
        }
    }
}

Тогда:

BadStringMe bad = new BadStringMe();
new Thread(bad.FurtleWithList).Start();
bad.AddString("This isn't thread-safe");

Предпочитайте личные поля - это упрощает анализ вашего кода.

Дополнительно:

  • Предпочитаю от List<T> до ArrayList в эти дни
  • По какой-то причине вы блокируете переменную static ... даже если у вас есть несколько экземпляров StringMeтолько одна нить может быть в AddString одновременно в общей сложности
  • Использование typeof(string) намного чище, чем Type.GetType("System.String")

3) Только методы, которые изменяют данные, должны быть заблокированы, или же операции блокировки чтения и записи должны быть заблокированы, чтобы сделать их потокобезопасными?

Все, предполагая, что может быть некоторые операции.Если все просто читает, вам не нужны никакие блокировки - но в противном случае ваши потоки чтения могут считывать два бита данных из структуры данных, которые были изменены между ними, даже если есть только один запись поток.(Есть также соображения о модели памяти, о которых следует помнить.)

4 голосов
/ 19 января 2012

1) Успешно ли я сделал потоки AddString andToString безопасными?

Да, если вы измените _stringArrayList на частное

2) В созданном мною методе ToString необходимо ли блокировать его, чтобы сделать его безопасным для потоков?

Да

3) Только методы, которые изменяют данные, должны быть заблокированы или операции блокировки чтения и записи должны быть заблокированы, чтобы сделать их потокобезопасными?

Чтение и запись.

3 голосов
/ 19 января 2012

Да для всех трех (т. Е. Для чтения / записи до последнего).

Но есть еще:

Вы делаете свой объект блокировки static, в то время как данные, которые вы защищаете, являются полями для каждого экземпляра. Это означает, что все экземпляры StringMe защищены друг от друга, хотя они имеют разные данные (т.е. экземпляры _stringArrayList). В приведенном вами примере вы можете удалить модификатор static из _locker. Чтобы быть более точным, вы обычно определяете «блокировку» для набора данных, или, что еще лучше, инварианты , которые вы хотите сохранить. Поэтому обычно время жизни (и область действия) блокировки должно равняться сроку службы данных.

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

...