Конструктор экземпляра устанавливает статический член, это потокобезопасно? - PullRequest
5 голосов
/ 03 сентября 2008

Я перефакторинг некоторого кода и мне интересно использовать lock в конструкторе экземпляра.

public class MyClass {

    private static Int32 counter = 0;
    private Int32 myCount;

    public MyClass() {

        lock(this) {
            counter++;
            myCount = counter;
        }
    }
}

Пожалуйста, подтвердите

  1. Конструкторы экземпляров являются поточно-ориентированными.
  2. Оператор блокировки запрещает доступ к этому блоку кода, а не к статическому члену 'counter'.

Если бы намерение исходного программиста состояло в том, чтобы каждый экземпляр знал свой «счет», как бы я синхронизировал доступ к члену «счетчик», чтобы гарантировать, что другой поток не новый, с MyClass и изменением считать до того, как этот установит свой счет?

К вашему сведению - этот класс не является синглтоном. Экземпляры должны просто знать их количество.

Ответы [ 7 ]

12 голосов
/ 03 сентября 2008

Если вы только увеличиваете число, для этого есть специальный класс (Interlocked) ...

http://msdn.microsoft.com/en-us/library/system.threading.interlocked.increment.aspx

Метод блокировки. Инкремент

Увеличивает указанную переменную и сохраняет результат как атомарную операцию.

System.Threading.Interlocked.Increment(myField);

Дополнительная информация о лучших практиках потоков ...

http://msdn.microsoft.com/en-us/library/1c9txz50.aspx

4 голосов
/ 03 сентября 2008

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

private static int counter = 0;
private static object counterLock = new Object();

lock(counterLock) {
    counter++;
    myCounter = counter;
}

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

Из того небольшого, что я могу сказать из вашего кода, вы пытаетесь дать объекту текущий счет во время его создания. Таким образом, с помощью приведенного выше кода счетчик будет заблокирован, пока счетчик обновляется и устанавливается локально. Таким образом, все остальные конструкторы должны будут ждать, пока счетчик не будет освобожден.

3 голосов
/ 03 сентября 2008

@ ajmastrean

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

т.е.

  • Сделать конструктор приватным.
  • Создать метод статического экземпляра, который возвращает тип.
  • В статическом методе экземпляра используйте ключевое слово lock перед созданием экземпляра.
  • Создание нового экземпляра типа.
  • Увеличить счет.
  • Разблокировать и вернуть новый экземпляр.

EDIT

Одна проблема, которая возникла у меня, если бы вы узнали, когда счет уменьшился? ;)

ИЗМЕНИТЬ СНОВА

Подумав об этом, вы можете добавить код в деструктор, который вызывает другой статический метод для уменьшения счетчика: D

3 голосов
/ 03 сентября 2008

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

private static Object lockObj = new Object();

и заблокируйте этот объект в конструкторе.

lock(lockObj){}

Однако я не уверен, есть ли ситуации, которые следует обрабатывать из-за оптимизации компилятора в .NET, как в случае с Java

2 голосов
/ 03 сентября 2008

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

class MyClass {

    static int _LastInstanceId = 0;
    private readonly int instanceId; 

    public MyClass() { 
        this.instanceId = Interlocked.Increment(ref _LastInstanceId);  
    }
}

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

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

(у Майка Шалла сначала был блокированный бит)

0 голосов
/ 03 сентября 2008

@ Rob

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

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

public MyClass {

    private static Int32 counter = 0;
    public static MyClass GetAnInstance() {

        lock(MyClass) {
            counter++;
            return new MyClass();
        }
    }

    private Int32 myCount;
    private MyClass() {
        myCount = counter;
    }
}
0 голосов
/ 03 сентября 2008

Я думаю, что если вы измените Singleton Pattern , чтобы включить счетчик (очевидно, с помощью метода, ориентированного на многопотоковое исполнение), у вас все будет хорошо:)

Редактировать

Дерьмо я случайно удалил!

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

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