Безопасность потоков в общих экземплярах (C #) - PullRequest
1 голос
/ 30 марта 2009

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

Предполагается, что 2 потока вызывают фабрику и оба возвращают ссылки на один и тот же объект из кэша. (т.е. нет нового оператора, в ответе ниже, объект возвращен из коллекции)

Если я хочу изменить закрытый элемент экземпляра внутри класса:

а) Плечо, я его первым заблокирую? б) Отразятся ли изменения в обеих темах?

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

Должен ли я что-то фундаментальное здесь? Почему я чувствую, что у меня есть?

===============

После первых нескольких ответов я подтвердил то, что думал, спасибо.

Полагаю, я действительно хочу знать, что если объекты в значительной степени предназначены только для чтения, то есть после создания они имеют только один элемент экземпляра, который может изменяться, нужно ли делать какие-либо блокировки при чтении свойств, которые не подвержен влиянию этого изменяемого члена экземпляра?

Опять я предполагаю, что нет, но я должен оценить второе мнение коллективного доверия мозгов StackOverflow :)

Ответы [ 6 ]

2 голосов
/ 31 марта 2009

Чтение и запись большинства примитивных типов, ссылок на строки и объекты являются атомарными (все, кроме энергонезависимых длинных и двойных). Это означает, что блокировки не нужны, если вы просто читаете или пишете поле или другую переменную изолированно. Итак, если вы когда-нибудь увидите такой код (и я гарантирую, что вы это сделаете), вы можете с радостью удалить ненужную и дорогостоящую блокировку:

public SomeClass SomeProperty
{
    get
    {
        lock (someLock)
        {
            return someField;
        }
    }

    set
    {
        lock (someLock)
        {
            someField = value;
        }
    }
}

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

Стоит также отметить, что Hashtable и Dictionary поддерживают несколько одновременных считывателей без какой-либо блокировки.

1 голос
/ 30 марта 2009

Да, вы должны заблокировать, прежде чем устанавливать приватный член. Это будет отражено в обоих потоках (поскольку это один и тот же объект).

Однако вам может понадобиться некоторое время, чтобы подумать, как обрабатывать блокировки и многопоточность.

Если ваша фабрика создает объект и добавляет его в коллекцию, ему потребуется блокировка некоторой формы вокруг подпрограммы для создания объекта. В противном случае два потока могут запросить объект одновременно, до его создания, и создать 2 экземпляра.

Кроме того, я бы не рекомендовал устанавливать закрытый член напрямую - было бы неплохо иметь один объект внутри возвращаемого экземпляра класса, используемый только для блокировки, и использовать некоторую форму средства доступа (метод или свойство), которое блокирует этот объект синхронизации и затем устанавливает поле.

Что касается того, чтобы "не доверять" этим двум экземплярам - вы всегда можете просто сделать что-нибудь, чтобы проверить в отладчике. Добавьте проверку на равенство ссылок или даже временно добавьте GUID к вашему классу, который настраивается при создании - легко проверить, что они одинаковы в этом случае.

1 голос
/ 30 марта 2009

Это должен быть тот же объект, и ответ на ваши вопросы - ДА, ДА.

Как проверить, что есть 2 экземпляра?

1 голос
/ 30 марта 2009

Вы предполагаете, что потоки имеют разные экземпляры объекта, что неверно. Потоки указывают на один и тот же объект; поэтому вам необходимо синхронизировать доступ к любым членам, к которым могут обращаться несколько потоков, будь то через доступ к свойствам, вызовы методов и т. д. и т. д.

0 голосов
/ 30 марта 2009

Вам нужно в обеих темах, чтобы увидеть результаты изменения приватного участника? Или это нормально для вас, если оба потока получают объект, но когда один поток изменяет закрытый член, другой поток не видит это изменение?

Вот почему я спрашиваю: на вашей фабрике вы можете взять объект из кеша, а затем клонировать или скопировать его перед возвратом. В результате каждый поток будет иметь свою собственную копию класса, и эта копия будет иметь состояние, которое было в кэшированном объекте в тот момент, когда они его запрашивали. Никакие 2 потока никогда не будут использовать один и тот же экземпляр, потому что вы создаете копию для каждого потока.

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

Однако будьте осторожны с идеей "клона". Если у вас есть граф объектов и вы выполняете только мелкое клонирование, вы получите ссылки, которые все еще используются в разных потоках, и вам снова потребуется синхронизация. Эта идея «одна копия на поток» имеет смысл, если объект является очень простым объектом без большого количества ссылок на другие объекты.

0 голосов
/ 30 марта 2009

Если новый оператор или конструктор вызывается дважды для объекта, то у вас есть два экземпляра одного и того же класса.

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