Domanda

In altre parole, questo thread di implementazione Singleton è sicuro:

public class Singleton
{
    private static Singleton instance;

    private Singleton() { }

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

    public static Singleton Instance
    {
        get { return instance; }
    }
}
È stato utile?

Soluzione

È garantito che i costruttori statici vengano eseguiti solo una volta per dominio dell'applicazione, prima che vengano create eventuali istanze di una classe o prima che venga effettuato l'accesso a qualsiasi membro statico. http://msdn.microsoft.com/en-us/library/aa645612.aspx

L'implementazione mostrata è thread-safe per la costruzione iniziale, ovvero non è richiesto alcun blocco o test null per la costruzione dell'oggetto Singleton.Tuttavia, ciò non significa che qualsiasi utilizzo dell'istanza verrà sincronizzato.Esistono diversi modi in cui ciò può essere fatto;Ne ho mostrato uno qui sotto.

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();
    }
}

Altri suggerimenti

Sebbene tutte queste risposte diano la stessa risposta generale, c'è un avvertimento.

Ricordare che tutte le potenziali derivazioni di una classe generica vengono compilate come tipi individuali.Quindi prestare attenzione quando si implementano costruttori statici per tipi generici.

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

MODIFICARE:

Ecco la dimostrazione:

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()));        
    }
}

Nella consolle:

Hit System.Object
Hit System.String

Utilizzando un costruttore statico in realtà È threadsafe.È garantito che il costruttore statico venga eseguito una sola volta.

Dalle specifiche del linguaggio C# http://msdn.microsoft.com/en-us/library/aa645612(VS.71).aspx:

Il costruttore statico di una classe viene eseguito al massimo una volta in un determinato dominio dell'applicazione.L'esecuzione di un costruttore statico viene avviata dal primo dei seguenti eventi che si verificano all'interno di un dominio applicazione:

  • Viene creata un'istanza della classe.
  • Viene fatto riferimento a uno qualsiasi dei membri statici della classe.

Quindi sì, puoi fidarti che il tuo singleton verrà istanziato correttamente.

Zooba ha sottolineato in modo eccellente (e anche 15 secondi prima di me!) che il costruttore statico non garantirà l'accesso condiviso thread-safe al singleton.Ciò dovrà essere gestito in un altro modo.

Ecco la versione di Cliffnotes dalla pagina MSDN sopra su c# singleton:

Usa sempre il seguente schema, non puoi sbagliare:

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

   private Singleton(){}

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

Oltre alle ovvie funzionalità del singleton, ti offre queste due cose gratuitamente (rispetto al singleton in C++):

  1. costruzione pigra (o nessuna costruzione se non è mai stata chiamata)
  2. sincronizzazione

È garantito che i costruttori statici si attivino solo una volta per dominio dell'app, quindi l'approccio dovrebbe essere OK.Tuttavia, dal punto di vista funzionale non è diverso dalla versione più concisa e in linea:

private static readonly Singleton instance = new Singleton();

La sicurezza dei thread è più un problema quando si inizializzano pigramente le cose.

IL Specifica dell'infrastruttura linguistica comune garantisce che "un inizializzatore di tipo deve eseguire esattamente una volta per un dato tipo, se non esplicitamente chiamato dal codice utente". (Sezione 9.5.3.1.) Quindi, a meno che tu non abbia un po 'di whacky sul Singleton ::. Cctor Cctor alla sciolta:. E la tua proprietà istanza è sicura.

Si noti che se il costruttore di Singleton accede alla proprietà Instance (anche indirettamente), la proprietà Instance sarà nulla.La cosa migliore che puoi fare è rilevare quando ciò accade e generare un'eccezione, controllando che l'istanza non sia nulla nella funzione di accesso alla proprietà.Una volta completato il costruttore statico, la proprietà Instance sarà diversa da null.

COME La risposta di Zoomba sottolinea che dovrai rendere Singleton sicuro per l'accesso da più thread o implementare un meccanismo di blocco sull'utilizzo dell'istanza Singleton.

Il costruttore statico lo farà fine corsa Prima qualsiasi thread può accedere alla classe.

    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();
    }

Il codice sopra ha prodotto i risultati seguenti.

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).

Anche se l'esecuzione del costruttore statico ha impiegato molto tempo, gli altri thread si sono fermati e hanno aspettato.Tutti i thread leggono il valore di _x impostato nella parte inferiore del costruttore statico.

Giusto per essere pedanti, ma non esiste un costruttore statico, ma piuttosto inizializzatori di tipo statico, ecco un piccolo demo della dipendenza ciclica del costruttore statico che illustra questo punto.

Il costruttore statico è garantito per essere thread-safe.Inoltre, controlla la discussione su Singleton su DeveloperZen:http://www.developerzen.com/2007/07/15/whats-wrong-with-this-code-1-discussion/

Sebbene le altre risposte siano per lo più corrette, c'è ancora un altro avvertimento con i costruttori statici.

Come da sezione II.10.5.3.3 Gare e situazioni di stallo del ECMA-335 Infrastruttura linguistica comune

L'inizializzazione del tipo da sola non deve creare uno stallo a meno che un codice chiamato da un iniziale di tipo (direttamente o indirettamente) invoca esplicitamente le operazioni di blocco.

Il codice seguente genera un deadlock

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();
    }
}

L'autore originale è Igor Ostrovsky, vedi il suo post Qui.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top