Какой хороший потокобезопасный шаблон универсального шаблона singleton в C#

У меня есть следующий шаблон синглтона C#, есть ли способ его улучшить?

    public class Singleton<T> where T : class, new()
    {

        private static object _syncobj = new object();
        private static volatile T _instance = null;
        public static T Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_syncobj)
                    {
                        if (_instance == null)
                        {
                            _instance = new T();
                        }
                    }
                }
                return _instance;
            }
        }

        public Singleton()
        { }

    }

Предпочтительный пример использования:

class Foo : Singleton<Foo> 
{
} 

Связанный:

Очевидная реализация singleton для .NET?

В его нынешнем виде мне пришлось добавить "protected" в конструктор для компиляции.

Kevin Driedger 27.01.2009 21:35

Вопрос трудно читать. Насколько хорош этот универсальный класс Singleton? Является ли он потокобезопасным? все еще передают правильный смысл?

Greg 25.09.2009 00:45

Не стесняйтесь делать все, что считаете нужным, с этим вопросом, вики сообщества

Sam Saffron 25.09.2009 01:30

Одно предостережение в отношении любого синглтона, основанного на свойстве экземпляра, заключается в том, что вы должны быть осторожны, если конструктор вашего класса имеет побочные эффекты. Если вы наведите указатель мыши на свойство Instance в отладчике, находясь в середине конструкции синглтона (например, потому что вы устанавливаете точку останова в одном из побочных эффектов), вы действительно можете снова выполнить конструктор и вызвать всевозможные забавные умопомрачительные проблемы. Это происходит потому, что отладчик (по умолчанию) автоматически оценивает свойства при наведении на них курсора.

Dan Bryant 25.10.2011 19:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
31
4
20 960
22

Ответы 22

Предоставлено Джудит Бишоп, http://patterns.cs.up.ac.za/

Эта реализация одноэлементного шаблона обеспечивает ленивую инициализацию.

//  Singleton PatternJudith Bishop Nov 2007
//  Generic version

public class Singleton<T> where T : class, new()
{
    Singleton() { }

    class SingletonCreator
    {
        static SingletonCreator() { }
        // Private object instantiated with private constructor
        internal static readonly T instance = new T();
    }

    public static T UniqueInstance
    {
        get { return SingletonCreator.instance; }
    }
}

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

Sam Saffron 18.06.2009 04:28

Просто короткий вопрос Сэм: Как вам удалось создать новый экземпляр класса? Согласно приведенному выше коду конструктор является частным ...

EggyBach 19.06.2009 13:36

Этот шаблон хорош, но меня беспокоит одна вещь: ограничение типа new () подразумевает, что синглтон имеет общедоступный конструктор, который нарушает шаблон синглтона. Я думаю, что эти ограничения следует удалить, а Activator.CreateInstance следует использовать для вызова защищенного конструктора.

Thomas Levesque 24.08.2009 20:48

T должен иметь общественныйnew()

Alexey Romanov 24.08.2009 22:04

@Darksider, Singleton <Dog> ... var d = new Dog () отлично работает ... чего я бы предпочел не делать. Смотрите: ответ Кейда.

Sam Saffron 25.08.2009 06:25

public sealed class Singleton
{
   private static readonly Singleton instance = new Singleton();

   private Singleton(){}

   public static Singleton Instance
   {
      get 
      {
         return instance; 
      }
   }
}

Есть нет двусмысленности в .NET в отношении порядка инициализации; но это вызывает проблемы с потоками.

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

Sam Saffron 19.09.2008 10:56

На самом деле он не инициализируется до того, как вам это понадобится; прочтите предоставленную ссылку MSDN; «экземпляр создается при первой ссылке на любой член класса».

blowdart 19.09.2008 11:58

По словам Джона Скита в Реализация шаблона Singleton в C#, опубликованный вами код на самом деле считается плохим кодом, потому что он кажется сломанным при проверке на соответствие стандарту ECMA CLI.

Также будьте осторожны: каждый раз, когда вы создаете экземпляр своего объекта с новым типом T, он становится другим экземпляром; это не отражается в вашем исходном синглтоне.

Это хороший пост ... Я изменил вопрос, чтобы отразить эти изменения

Sam Saffron 19.09.2008 15:08

Мне очень понравился ваш исходный ответ - единственное, чего не хватает (согласно ссылке, опубликованной blowdart), - это сделать переменную _instance изменчивой, чтобы убедиться, что она действительно установлена ​​в блокировке. Я действительно использую решение blowdarts, когда мне нужно использовать синглтон, но у меня нет необходимости создавать поздние экземпляры и т. д.

Все это вам не нужно, в C# уже есть хороший встроенный шаблон singleton.

static class Foo

Если вам нужно что-то более интересное, скорее всего, ваш новый синглтон будет достаточно другим, чтобы ваш общий шаблон был бесполезен.

Обновлено: Под «чем-нибудь более интересным» я включаю наследование. Если вы можете наследовать от синглтона, это больше не синглтон.

Неправильный. Есть довольно много ситуаций, когда синглтоны полезны, а статические классы не работают, например. попробуйте сделать это статичным. общедоступный TimeSpan это [строка имя_файла] {получить {вернуть новый TimeSpan (); } набор { } }

Sam Saffron 19.09.2008 11:05

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

Wayne Bloss 15.11.2008 08:47

Я исправлю свой комментарий ... статический класс - ХОРОШЕЕ решение для группировки статических методов, но технически он не реализует синглтон, поскольку нет «экземпляра», и вы не получаете семантику экземпляра, такую ​​как наследование.

Wayne Bloss 15.11.2008 08:58

Синглтон может реализовывать интерфейс или расширять другой класс. Это будет работать в статическом статическом классе со статическими методами.

Fedearne 27.08.2010 13:55

Этот код не компилируется, вам нужно ограничение "класса" на T.

Кроме того, для этого кода требуется общедоступный конструктор в целевом классе, что не подходит для синглтона, потому что вы не можете контролировать во время компиляции, что вы получаете (единственный) экземпляр только через свойство (или поле) Instance. Если у вас нет других статических членов, кроме Instance, вы можете использовать только это:

class Foo
{
  public static readonly Instance = new Foo();
  private Foo() {}
  static Foo() {}
}

Это потокобезопасный (гарантируется CLR) и ленивый (экземпляр создается с первым доступом к типу). Дополнительные сведения о BeforeFieldInit и о том, почему нам здесь нужен статический конструктор, см. В разделе https://csharpindepth.com/articles/BeforeFieldInit.

Если вы хотите иметь другие общедоступные статические члены по типу, но создавать объект только при доступе к экземпляру, вы можете создать вложенный тип, как в https://csharpindepth.com/articles/Singleton

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

Wayne Bloss 15.11.2008 08:55

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

Michael Freidgeim 01.05.2013 01:42

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

Всякий раз, когда вам нужен синглтон, просто сделайте это:

class MyConcreteClass
{
  #region Singleton Implementation

    public static readonly Instance = new MyConcreteClass();

    private MyConcreteClass(){}

  #endregion

    /// ...
}

Подробнее об этом ответе в другом потоке: Как реализовать синглтон на C#?

Однако поток не использует общий.

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

Public MustInherit Class Singleton(Of T As {Class, New})
    Public Sub New()
    End Sub

    Private Class SingletonCreator
        Shared Sub New()
        End Sub
        Friend Shared ReadOnly Instance As New T
    End Class

    Public Shared ReadOnly Property Instance() As T
        Get
            Return SingletonCreator.Instance
        End Get
    End Property
End Class

Мой вклад в обеспечение создания данных экземпляра по запросу:


/// <summary>Abstract base class for thread-safe singleton objects</summary>
/// <typeparam name = "T">Instance type</typeparam>
public abstract class SingletonOnDemand<T> {
    private static object __SYNC = new object();
    private static volatile bool _IsInstanceCreated = false;
    private static T _Instance = default(T);

/// <summary>Instance data</summary> public static T Instance { get { if (!_IsInstanceCreated) lock (__SYNC) if (!_IsInstanceCreated) _Instance = Activator.CreateInstance<T>(); return _Instance; } } }

пфф ... снова ... :)
Мой вклад в обеспечение создания данных экземпляра по запросу:


/// <summary>Abstract base class for thread-safe singleton objects</summary>
/// <typeparam name = "T">Instance type</typeparam>
public abstract class SingletonOnDemand<T> {
  private static object __SYNC = new object();
  private static volatile bool _IsInstanceCreated = false;
  private static T _Instance = default(T);

  /// <summary>Instance data</summary>
  public static T Instance {
    get {
      if (!_IsInstanceCreated)
        lock (__SYNC)
          if (!_IsInstanceCreated) {
            _Instance = Activator.CreateInstance<T>();
            _IsInstanceCreated = true;
          }
          return _Instance;
    }
  }
}

Если вы замените '= default (T)' на '= new T ()', вам не нужно выполнять двойную проверку блокировки и просто 'get {return _Instance; } '

Steven Evers 12.07.2010 02:40

По запросу, перекрестная публикация моего исходного ответа на другой вопрос.

Моя версия использует Reflection, работает с непубличными конструкторами в производном классе, является потокобезопасной (очевидно) с ленивым созданием (согласно статье, которую я нашел по ссылке ниже):

public class SingletonBase<T> where T : class
{
    static SingletonBase()
    {
    }

    public static readonly T Instance = 
        typeof(T).InvokeMember(typeof(T).Name, 
                                BindingFlags.CreateInstance | 
                                BindingFlags.Instance |
                                BindingFlags.Public |
                                BindingFlags.NonPublic, 
                                null, null, null) as T;
}

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

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

: / Общий шаблон "singleton" Джудит Бишоп кажется некорректным, всегда можно создать несколько экземпляров типа T, поскольку конструктор должен быть общедоступным, чтобы использовать его в этом "шаблоне". На мой взгляд, это не имеет ничего общего с синглтоном, это просто своего рода фабрика, которая всегда возвращает один и тот же объект, но не делает его синглтоном ... пока может быть более одного экземпляра класса, который он не может быть синглом. По какой причине этот паттерн пользуется наибольшим рейтингом?

public sealed class Singleton
{
  private static readonly Singleton _instance = new Singleton();

  private Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      return _instance;
    }
  }
}

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

public static class LazyGlobal<T> where T : new()
{
    public static T Instance
    {
        get { return TType.Instance; }
    }

    private static class TType
    {
        public static readonly T Instance = new T();
    }
}

// user code:
{
    LazyGlobal<Foo>.Instance.Bar();
}

Или же:

public delegate T Func<T>();

public static class CustomGlobalActivator<T>
{
    public static Func<T> CreateInstance { get; set; }
}

public static class LazyGlobal<T>
{
    public static T Instance
    {
        get { return TType.Instance; }
    }

    private static class TType
    {
        public static readonly T Instance = CustomGlobalActivator<T>.CreateInstance();
    }
}

{
    // setup code:
    // CustomGlobalActivator<Foo>.CreateInstance = () => new Foo(instanceOf_SL_or_IoC.DoSomeMagicReturning<FooDependencies>());
    CustomGlobalActivator<Foo>.CreateInstance = () => instanceOf_SL_or_IoC.PleaseResolve<Foo>();
    // ...
    // user code:
    LazyGlobal<Foo>.Instance.Bar();
}

Некоторое время назад видел один, который использует отражение для доступа к частному (или общедоступному) конструктору по умолчанию:

public static class Singleton<T>
{
    private static object lockVar = new object();
    private static bool made;
    private static T _singleton = default(T);

    /// <summary>
    /// Get The Singleton
    /// </summary>
    public static T Get
    {
        get
        {
            if (!made)
            {
                lock (lockVar)
                {
                    if (!made)
                    {
                        ConstructorInfo cInfo = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null);
                        if (cInfo != null)
                            _singleton = (T)cInfo.Invoke(new object[0]);
                        else
                            throw new ArgumentException("Type Does Not Have A Default Constructor.");
                        made = true;
                    }
                }
            }

            return _singleton;
        }
    }
}

Попробуйте этот общий класс Singleton, реализующий шаблон проектирования Singleton поточно-безопасным и ленивым способом (спасибо wcell).

public abstract class Singleton<T> where T : class
{
    /// <summary>
    /// Returns the singleton instance.
    /// </summary>
    public static T Instance
    {
        get
        {
            return SingletonAllocator.instance;
        }
    }

    internal static class SingletonAllocator
    {
        internal static T instance;

        static SingletonAllocator()
        {
            CreateInstance(typeof(T));
        }

        public static T CreateInstance(Type type)
        {
            ConstructorInfo[] ctorsPublic = type.GetConstructors(
                BindingFlags.Instance | BindingFlags.Public);

            if (ctorsPublic.Length > 0)
                throw new Exception(
                    type.FullName + " has one or more public constructors so the property cannot be enforced.");

            ConstructorInfo ctorNonPublic = type.GetConstructor(
                BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[0], new ParameterModifier[0]);

            if (ctorNonPublic == null)
            {
                throw new Exception(
                    type.FullName + " doesn't have a private/protected constructor so the property cannot be enforced.");
            }

            try
            {
                return instance = (T)ctorNonPublic.Invoke(new object[0]);
            }
            catch (Exception e)
            {
                throw new Exception(
                    "The Singleton couldnt be constructed, check if " + type.FullName + " has a default constructor", e);
            }
        }
    }
}

Идиома Double-Check Locking [Lea99], предоставляемая Microsoft здесь, удивительно похожа на предоставленный вами код, к сожалению, это не соответствует стандарту ECMA CLI для пуританского представления поточно-безопасного кода и может работать некорректно во всех ситуациях.

В многопоточной программе разные потоки могут одновременно пытаться создать экземпляр класса. По этой причине реализация Singleton, которая полагается на оператор if, чтобы проверить, является ли экземпляр нулевым не будет потокобезопасным. Не пишите такой код!

Простым, но эффективным средством создания потокобезопасного синглтона является использование вложенного класса для его создания. Ниже приведен пример синглтона с ленивым созданием экземпляра:

public sealed class Singleton
{ 
   private Singleton() { }

   public static Singleton Instance
   {
      get
      {
         return SingletonCreator.instance;
      }
   }

   private class SingletonCreator
   {
      static SingletonCreator() { }
      internal static readonly Singleton instance = new Singleton();
   }
}

Использование:

Singleton s1 = Singleton.Instance;
Singleton s2 = Singleton.Instance;
if (s1.Equals(s2))
{
    Console.WriteLine("Thread-Safe Singleton objects are the same");
}

Общее решение:

public class Singleton<T>
    where T : class, new()
{
    private Singleton() { }

    public static T Instance 
    { 
        get 
        { 
            return SingletonCreator.instance; 
        } 
    } 

    private class SingletonCreator 
    {
        static SingletonCreator() { }

        internal static readonly T instance = new T();
    }
}

Использование:

class TestClass { }

Singleton s1 = Singleton<TestClass>.Instance;
Singleton s2 = Singleton<TestClass>.Instance;
if (s1.Equals(s2))
{
    Console.WriteLine("Thread-Safe Generic Singleton objects are the same");
}

Наконец, вот несколько выпущенное и полезное предложение - чтобы помочь избежать взаимоблокировок, которые могут быть вызваны использованием ключевого слова lock, рассмотрите возможность добавления следующего атрибута для защиты кода только в общедоступных статических методах:

using System.Runtime.CompilerServices;
[MethodImpl (MethodImplOptions.Synchronized)]
public static void MySynchronizedMethod()
{
}

Рекомендации:

  1. Поваренная книга C# (O'Reilly), Джей Хильярд и Стивен Тейле
  2. C# 3.0 Design Patterns (O'Reilly), Джудит Бишоп
  3. CSharp-Online.Net - шаблон проектирования синглтона: потокобезопасный синглтон

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

public class Singleton<T> where T : class
{
    class SingletonCreator
    {
        static SingletonCreator() { }

        internal static readonly T Instance =
            typeof(T).InvokeMember(typeof(T).Name,
                                    BindingFlags.CreateInstance |
                                    BindingFlags.Instance |
                                    BindingFlags.Public |
                                    BindingFlags.NonPublic,
                                    null, null, null) as T;
    }

    public static T Instance
    {
        get { return SingletonCreator.Instance; }
    }
}

Предполагаемая реализация:

public class Foo: Singleton<Foo>
{
     private Foo() { }
}

Потом:

Foo.Instance.SomeMethod();

Это моя точка зрения, использующая .NET 4

public class Singleton<T> where T : class, new()
    {
        Singleton (){}

        private static readonly Lazy<T> instance = new Lazy<T>(()=> new T());

        public static T Instance { get { return instance.Value; } } 
    }

Класс не является одноэлементным, потому что общедоступный конструктор позволяет создавать несколько экземпляров. Класс полезный, но я переименовал его в SingleInstanceBase

Michael Freidgeim 01.05.2013 01:55

См. Вопрос. Это способ улучшить класс вопросов с помощью .net 4

Alexandr 01.05.2013 15:05

Как в википедия:

the singleton pattern is a design pattern that restricts the instantiation of a class to one object

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

public class MyClass
{
        private MyClass()
        {

        }

        static MyClass()
        {
            Instance = new MyClass();
        }

        public static MyClass Instance { get; private set; }
}

ИЛИ ЖЕ:

public class MyClass
    {
            private MyClass()
            {

            }

            static MyClass()
            {
                Instance = new MyClass();
            }

            private static MyClass instance;



            public static MyClass Instance
            {
                get
                {
                    return instance;
                }
                private set
                {
                    instance = value;
                }
            }
    }

Это работает для меня:

public static class Singleton<T> 
{
    private static readonly object Sync = new object();

    public static T GetSingleton(ref T singletonMember, Func<T> initializer)
    {
        if (singletonMember == null)
        {
            lock (Sync)
            {
                if (singletonMember == null)
                    singletonMember = initializer();
            }
        }
        return singletonMember;
    }
}

Использование:

private static MyType _current;
public static MyType Current = Singleton<MyType>.GetSingleton(ref _current, () => new MyType());

Потребляйте синглтон:

MyType.Current. ...

Независимо от того, какой пример вы выберете, всегда проверяйте параллелизм с помощью Parallel.For! (цикл, в котором итерации могут выполняться параллельно)

вставьте Singleton C'tor:

 private Singleton ()
        {
            Console.WriteLine("usage of the Singleton for the first time");
        }

поместите в Main:

Parallel.For(0, 10,
              index => {
                  Thread tt = new Thread(new ThreadStart(Singleton.Instance.SomePrintMethod));
                  tt.Start();
              });

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