Стремясь к оптимизации выполнения программы, многие люди независимо приходят к одинаковому решению для ускорения «ленивой» инициализации членов класса в многопоточной среде. Приведем следущий пример (который, как выясняется, не совсем корректен):
class Singleton
{
private static readonly object _locker = new object();
private static Singleton _instance = null;
public static Singleton Instance
{
get
{
if( _instance == null )
{
lock( _locker )
if( _instance == null )
_instance = new Singleton();
}
return _instance;
}
}
}
Логика кода совершенно очевидна. Если _instance == null, то мы запираем доступ для других тредов и еще раз проверяем _instance на null, дабы убедиться, что за время, прошедшее между проверкой условия и запиранием, другой поток не успел проинициализировать поле.
Казалось бы, что ошибиться здесь нельзя, но, как оказывается, в Ява-мире подобный подход уже давно разрбран по полочкам и признан недееспособным. Причины подробно разъясняются в широко цитируемой статье "Double-Checked Locking is Broken". Вкратце, все упирается в проблемы, которые возникают при работе оптимизирущего компилятора, а также в многопроцессорных системах.
Microsoft говорит, что им была известна эта проблема, поэтому в .NET есть способ ее корректного решения – нужно использовать ключевое слово volatile. (Замечу, что пример, приведенный в статье несколько неправилен. Там используется lock(typeof), что они сами признали плохой практикой).
class Singleton
{
private static readonly object _locker = new object();
private static volatile Singleton _instance = null;
public static Singleton Instance
{
get
{
if( _instance == null )
{
lock( _locker )
if( _instance == null )
_instance = new Singleton();
}
return _instance;
}
}
}
Есть также и другие, менее экзотические средства:
- Можно всегда сначала запирать, а потом проверять на null. Этот метод гарантированно работает, хотя из-за постоянного запирания он должен быть чуть-чуть медленнее.
- Можно проинициализировать поле _instance прямо в декларации класса. В этом случае реальная инициализация произойдет где-то перед первым обращением к полю, т.е. вся нужная работа будет сделана вообще без нашего участия.
Posted
Jan 25 2006, 01:54 PM
by
Andrew Mayorov