Студопедия

КАТЕГОРИИ:

АстрономияБиологияГеографияДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРиторикаСоциологияСпортСтроительствоТехнологияФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника


Двойная блокировка




Существует способ, известный как блокировка с двойной проверкой. Этот способ используется, если нужно отложить создание singleton-объекта до тех пор, пока приложение не запросит его [иногда это называют отложенной инициализацией]. Если приложение не запросит объект, он никогда не будет создан, что позволяет экономить время и память. Возможность возникновения проблем появляется, когда несколько потоков одновременно запрашивают singleton-объект. В этом случае нужно использовать какую-либо синхронизацию потоков, чтобы singleton-объект создавался только раз.

Вот пример кода, показывающий, как применять этот способ блокировки в C#:

 

public sealed class Singleton

{

// Объект s_lock требуется для обеспечения безопасности в многопоточной среде.

// Наличие этого объекта предполагает, что для создания singleton-объекта требуется

// больше ресурсов, чем для создания объекта System.Object, и что его создание

// может вовсе не понадобиться. В противном случае проще и эффективнее просто создать

// singleton-объект в конструкторе класса.

private static Object s_lock = new Object();

// Временные операции в модели памяти CLR не требуются, но они обязательны в

// модели памяти ЕСМА. Чтобы соответствовать стандарту, я включил ее сюда.

private static volatile Singleton s_value;

// Закрытый конструктор не позволяет создавать экземпляры любому.

// не относящемуся к классу коду.

private Singleton() { }

// Открытое статическое свойство,

// возвращающее singleton-объект (который создается при необходимости).

public static Singleton Value

{

get

{

// Был ли уже создан singleton-объект?

if (s_value == null)

{

// Нет, только один поток должен создавать его.

lock (s_lock)

{

// Создал ли его другой поток?

if (s_value == null)

{

// Нет, все в порядке, этот поток создаст его.

// Временность операций гарантирует, что все поля singleton-объекта

// (инициализированные конструктором) будут очищены, прежде чем

// другие потоки увидят ссылку на этот объект.

s_value = new Singleton();

// Возвращение ссылки на singleton-объект.

return s_value;

}

}

}

}

}

}

Принцип работы блокировки с двойной проверкой заключается в том, что вызов метода-аксессора get быстро проверяет поле s_value, выясняя, был ли объект уже создан, и если да, метод возвращает ссылку на него. Изюминка здесь в том, что после создания объекта синхронизация потоков не требуется, и приложение будет работать очень быстро. С другой стороны, если первый поток, вызвавший метод получения доступа к свойству value, увидит, что объект не был создан, он устанавливает блокировку синхронизации потоков, чтобы только один поток мог создать singleton-объект. Это значит, что производительность страдает только однажды, при первом запросе singleton-объекта.

Теоретически возможно, что JIT-компилятор считает значение поля s_value из регистра процессора в начале метода-аксессора get и просто запросит этот регистр при проверке условия второго оператора if. Если JIT-компилятор создаст именно такой код, значение во втором операторе if всегда будет равно true и singleton-объект может быть создан несколькими потоками. Это совсем не то, что нужно. Однако JIT-компилятор знает, что поле, помеченное ключевым словом volatile, кешировать не надо. Кроме того, JIT-компилятор всегда заново считывает поле, к которому обращается после вызова метода, если этот метод включает операции временного чтения или записи (например, Monitor.Enter или Monitor.Exit).

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

 

public sealed class Singleton

{

private static Singleton s_value = new Singleton();

// Закрытый конструктор не позволяет какому-либо внешнему коду

// создавать экземпляры класса.

private Singleton() { }

// Открытое статическое свойство, возвращающее singleton-объект.

public static Singleton Value

{

get

{

// Возвращение ссылки на singleton-объект.

return s_value;

}

}

}

 

CLR автоматически вызывает конструктор класса типа при первой попытке получить доступ к члену этого класса, поэтому, когда поток в первый раз запросит метод-аксессор get свойства Value объекта Singleton, CLR автоматически вызовет конструктор класса, который создаст экземпляр объекта. Кроме того, CLR уже обеспечил безопасность конструктора класса в отношении синхронизации потоков.

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

Блокировка с двойной проверкой интересна только в том случае, если класс имеет много членов и нужно создавать singleton-объект, только когда вызывается один из членов. Кроме этого, чтобы получить выигрыш, создание singleton-объекта должно быть намного более затратным, чем создание объекта, используемого для блокировки.


Поделиться:

Дата добавления: 2015-04-21; просмотров: 78; Мы поможем в написании вашей работы!; Нарушение авторских прав





lektsii.com - Лекции.Ком - 2014-2024 год. (0.006 сек.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав
Главная страница Случайная страница Контакты