Двойная проверка блокировки в одноточечном шаблоне
это может быть основной вопрос
чтобы иметь singleton в многопоточной среде, мы можем использовать блокировку. См. Фрагмент кода. Но почему нам нужна двойная проверка блокировки в одноэлементном шаблоне? И еще, что означает блокировка с двойной проверкой?
class singleton
{
private static singleton instance = null;
private static singleton() { }
private static object objectlock = new object();
public static singleton Instance
{
get
{
lock (objectlock) //single - check lock
{
if (instance == null)
{
instance = new singleton();
}
return instance;
}
}
}
}
Ответы
Ответ 1
Jon Skeet подробно объясняет это.
Замки дороги.
Если объект уже существует, нет смысла снимать блокировку.
Таким образом, у вас есть первая проверка за пределами блокировки.
Однако, даже если объект не существовал до того, как вы посмотрели, другой поток, возможно, создал его между условием if
и оператором lock
.
Поэтому вам нужно снова проверить внутри замка.
Однако лучший способ написать singleton - использовать конструктор static
:
public sealed class Singleton
{
private Singleton()
{
}
public static Singleton Instance { get { return Nested.instance; } }
private class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
Ответ 2
И с .Net 4.x и новее вы должны по возможности относиться к классу Lazy, поскольку этот шаблон используется с параметром Initialization And Publication. (примечание: обратное также доступно, где создание не является потокобезопасным, но публикация экземпляра осуществляется через вариант публикации)
Ответ 3
"Лучший" способ, который я знаю, это:
public class MySingleton {
// object for synchronization
private static readonly object syncRoot = new object();
// the singleton instance
private static MySingleton @default;
public static MySingleton Default {
get {
// geting singleton instance without locking
var result = @default;
// if result is NOT null, no additional action is required
if ( object.ReferenceEquals(result, null) ){
// lock the synchronization object
lock(syncRoot) {
// geting singleton instanc in lock - because
// the value of @default field could be changed
result = @default;
// checking for NULL
if ( object.ReferenceEquals(result, null) ) {
// if result is NULL, create new singleton instance
result = new MySingleton();
// set the default instance
@default = result;
}
}
}
// return singleton instance
return result;
}
}
}
Ответ 4
Если вы создаете объект в инициализаторе поля, вам не нужна блокировка:
class singleton
{
private static singleton instance = new singleton();
private static singleton() { }
public static singleton Instance
{
get { return instance; }
}
}
Также - имейте в виду, что блокировка управляет только созданием объекта, объект все равно должен быть потокобезопасным, если вы используете его в нескольких потоках.
Ответ 5
Многопоточный синглтон: лучший подход к использованию двойной проверки блокировки
public sealed class Singleton
{
private static volatile Singleton _instance;
private static readonly object InstanceLoker= new Object();
private Singleton() {}
public static Singleton Instance
{
get
{
if (_instance == null)
{
lock (InstanceLoker)
{
if (_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
}
}
Ответ 6
Когда мы пытаемся выполнить метод одиночного класса с использованием параллельных библиотек. Он не распознает поведение singleton из-за многопоточности, выполняемой в TPL, что приводит к сбою концепции Singleton Pattern. Для преодоления этой проблемы существует концепция блокировки объекта, чтобы в то время к нему мог обращаться только один поток.
Но это неэффективный подход, потому что участие проверки блокировки создает ненужные часы для объекта. Чтобы избежать этого, мы используем "Проверка двойной блокировки"
![введите описание изображения здесь]()