Конструктор экземпляра устанавливает статический элемент, является ли он потокобезопасным?

StackOverflow https://stackoverflow.com/questions/41792

Вопрос

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

public class MyClass {

    private static Int32 counter = 0;
    private Int32 myCount;

    public MyClass() {

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

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

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

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

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

Это было полезно?

Решение

@ajmastrean

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

т. е.

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

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

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

ОТРЕДАКТИРУЙТЕ ЕЩЕ РАЗ

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

Другие советы

Если вы только увеличиваете число, то именно для этого существует специальный класс (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

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

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

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

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

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

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

private static Object lockObj = new Object();

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

lock(lockObj){}

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

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

class MyClass {

    static int _LastInstanceId = 0;
    private readonly int instanceId; 

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

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

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

(У Майка Шолла первым был сблокированный фрагмент)

Я думаю, что если вы измените Одноэлементный Паттерн чтобы включить счетчик (очевидно, используя потокобезопасный метод), у вас все будет в порядке :)

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

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

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

@Роб

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

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

public MyClass {

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

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

    private Int32 myCount;
    private MyClass() {
        myCount = counter;
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top