Является ли поток статического конструктора C# безопасным?



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



public class Singleton
{
private static Singleton instance;

private Singleton() { }

static Singleton()
{
instance = new Singleton();
}

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

10 ответов:

статические конструкторы гарантированно запускаются только один раз в домене приложения, прежде чем будут созданы какие-либо экземпляры класса или будут доступны любые статические члены. http://msdn.microsoft.com/en-us/library/aa645612.aspx

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

public class Singleton
{
    private static Singleton instance;
    // Added a static mutex for synchronising use of instance.
    private static System.Threading.Mutex mutex;
    private Singleton() { }
    static Singleton()
    {
        instance = new Singleton();
        mutex = new System.Threading.Mutex();
    }

    public static Singleton Acquire()
    {
        mutex.WaitOne();
        return instance;
    }

    // Each call to Acquire() requires a call to Release()
    public static void Release()
    {
        mutex.ReleaseMutex();
    }
}

хотя все эти ответы дают один и тот же общий ответ, есть одно предостережение.

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

class MyObject<T>
{
    static MyObject() 
    {
       //this code will get executed for each T.
    }
}

EDIT:

вот пример:

static void Main(string[] args)
{
    var obj = new Foo<object>();
    var obj2 = new Foo<string>();
}

public class Foo<T>
{
    static Foo()
    {
         System.Diagnostics.Debug.WriteLine(String.Format("Hit {0}", typeof(T).ToString()));        
    }
}

в консоли:

Hit System.Object
Hit System.String

использование статического конструктора на самом деле и threadsafe. Статический конструктор гарантированно будет выполнен только один раз.

из спецификации языка C#http://msdn.microsoft.com/en-us/library/aa645612 (VS. 71). aspx:

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

  • создается экземпляр класса.
  • на любой из статических членов класса имеются ссылки.

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

Zooba сделал отличную точку (и за 15 секунд до меня тоже!) что статический конструктор не гарантирует потокобезопасный общий доступ к синглтону. Это нужно будет обрабатывать в другом манера.

вот версия Cliffnotes с вышеуказанной страницы MSDN на C# singleton:

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

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

   private Singleton(){}

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

помимо очевидных особенностей синглтона, он дает вам эти две вещи бесплатно (в отношении синглтона в c++):

  1. ленивая конструкция (или без конструкции, если она никогда не называлась)
  2. синхронизация

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

private static readonly Singleton instance = new Singleton();

потокобезопасность-это больше проблема, когда вы лениво инициализируете вещи.

The спецификация инфраструктуры общего языка гарантирует, что " инициализатор типа должен выполняться ровно один раз для любого заданного типа, если он явно не вызван пользовательским кодом."(Раздел 9.5.3.1.) Так что если у вас нет какой-то чокнутый IL на свободе вызова Синглтон::.cctor напрямую (маловероятно) ваш статический конструктор будет выполняться ровно один раз перед использованием типа Singleton, будет создан только один экземпляр Singleton, и Ваше свойство экземпляра поточно-ориентированный.

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

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

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

статический конструктор гарантированно будет потокобезопасным. Кроме того, ознакомьтесь с обсуждением синглтона в DeveloperZen: http://www.developerzen.com/2007/07/15/whats-wrong-with-this-code-1-discussion/

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

    private class InitializerTest
    {
        static private int _x;
        static public string Status()
        {
            return "_x = " + _x;
        }
        static InitializerTest()
        {
            System.Diagnostics.Debug.WriteLine("InitializerTest() starting.");
            _x = 1;
            Thread.Sleep(3000);
            _x = 2;
            System.Diagnostics.Debug.WriteLine("InitializerTest() finished.");
        }
    }

    private void ClassInitializerInThread()
    {
        System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() starting.");
        string status = InitializerTest.Status();
        System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() status = " + status);
    }

    private void classInitializerButton_Click(object sender, EventArgs e)
    {
        new Thread(ClassInitializerInThread).Start();
        new Thread(ClassInitializerInThread).Start();
        new Thread(ClassInitializerInThread).Start();
    }

приведенный выше код дал следующие результаты.

10: ClassInitializerInThread() starting.
11: ClassInitializerInThread() starting.
12: ClassInitializerInThread() starting.
InitializerTest() starting.
InitializerTest() finished.
11: ClassInitializerInThread() status = _x = 2
The thread 0x2650 has exited with code 0 (0x0).
10: ClassInitializerInThread() status = _x = 2
The thread 0x1f50 has exited with code 0 (0x0).
12: ClassInitializerInThread() status = _x = 2
The thread 0x73c has exited with code 0 (0x0).

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

хотя другие ответы в основном верны, есть еще один нюанс со статическими конструкторами.

в разделе II.10.5.3. 3 гонки и тупики на общий язык ECMA-335 Инфраструктура

инициализация типа сама по себе не должна создавать взаимоблокировку, если только какой-либо код позвонили из инициализатора типа (прямо или косвенно) явно вызывает операции блокировки.

следующий код приводит к тупику

using System.Threading;
class MyClass
{
    static void Main() { /* Won’t run... the static constructor deadlocks */  }

    static MyClass()
    {
        Thread thread = new Thread(arg => { });
        thread.Start();
        thread.Join();
    }
}

Автор оригинала-Игорь Островский, смотрите его пост здесь.

Comments

    Ничего не найдено.