Domanda

Mi sono imbattuto in questo articolo discutere perché il doppio -check paradigma bloccaggio è suddiviso in Java. È il paradigma valido per NET (in particolare, C #), se le variabili sono dichiarate volatile?

È stato utile?

Soluzione

Attuazione del pattern Singleton in C # parla di questo problema nella terza versione.

Si dice:

  

Fare l'istanza variabile volatile, può farlo funzionare, come farebbe chiama barriera di memoria esplicita, anche se in quest'ultimo caso anche gli esperti non sono d'accordo esattamente quali barriere sono necessari. Tendo a cercare di evitare situazioni in cui gli esperti non sono d'accordo ciò che è giusto e ciò che è sbagliato!

L'autore sembra implicare che doppia chiusura è meno probabile di lavorare rispetto ad altre strategie e quindi non dovrebbe essere usato.

Altri suggerimenti

Fare doppio controllo chiusura ora lavora in Java e C # (il modello di memoria Java cambiato e questo è uno degli effetti). Tuttavia, è necessario per farlo esattamente a destra. Se si fa qualche anche un po ', si può anche finire per perdere il filo di sicurezza.

Come altre risposte hanno dichiarato, se si sta implementando la Singleton modello lì sono modi molto migliori per farlo. Personalmente, se mi trovo in una situazione in cui devo scegliere tra interblocco ricontrollato e "bloccare ogni volta" Codice Mi piacerebbe andare per bloccare ogni volta fino a quando avessi avuto prova reale che si stava causando un collo di bottiglia. Quando si tratta di threading, un modello semplice e, ovviamente,-corretta vale molto.

NET 4.0 ha un nuovo tipo: Lazy<T> che toglie ogni preoccupazione su come ottenere il modello sbagliato. Fa parte della nuova Biblioteca Task Parallel.

Vedere le MSDN Parallel Computing Dev Center: http://msdn.microsoft. com / it-it / concorrenza / default.aspx

A proposito, c'è un backport (credo che sia supportato) for .NET 3.5 SP1 disponibile qui .

Nota che in Java (e molto probabilmente in .Net pure), doppio controllo chiusura per l'inizializzazione Singleton è completamente inutile e rotto. Poiché le classi non sono inizializzati fino momento del primo utilizzo, l'inizializzazione ritardata desiderato è già raggiunto questo;

private static Singleton instance = new Singleton();

A meno che la classe Singleton contiene cose come le costanti che possono essere raggiunti prima di un'istanza Singleton viene utilizzato per la prima, questo è tutto quello che devi fare.

Non capisco il motivo per cui tutte le persone dice che il bloccaggio doppio controllo è cattivo modello, ma non adattare il codice per farlo funzionare correttamente. A mio parere, questo codice di seguito dovrebbe funzionare bene.

Se qualcuno potrebbe dirmi se questo codice soffrono il problema menzionate nell'articolo di Cameron, si prega di fare.

public sealed class Singleton {
    static Singleton instance = null;
    static readonly object padlock = new object();

    Singleton() {
    }

    public static Singleton Instance {
        get {
            if (instance != null) {
                return instance;
            }

            lock (padlock) {
                if (instance != null) {
                    return instance;
                }

                tempInstance = new Singleton();

                // initialize the object with data

                instance = tempInstance;
            }
            return instance;
        }
    }
}
  

Ho ottenuto ricontrollato blocco a lavorare utilizzando un valore booleano (vale a dire usando un primitivo per evitare l'inizializzazione pigra):

Il singleton utilizzando booleana non funziona. L'ordine delle operazioni come si è visto tra i diversi thread non è garantita a meno che non si passa attraverso una barriera di memoria. In altre parole, come visto da un secondo filo, created = true può essere eseguito prima instance= new Singleton();

Io non capisco il motivo per cui ci sono un sacco di modelli di implementazione su ricontrollato blocco (a quanto pare per aggirare idiosincrasie del compilatore in varie lingue). L'articolo di Wikipedia su questo argomento mostra il metodo ingenuo e possibili modi per risolvere il problema, ma nessuno è così semplice come questo (in C #):

public class Foo
{
  static Foo _singleton = null;
  static object _singletonLock = new object();

  public static Foo Singleton
  {
    get
    {
      if ( _singleton == null )
        lock ( _singletonLock )
          if ( _singleton == null )
          {
            Foo foo = new Foo();

            // Do possibly lengthy initialization,
            // but make sure the initialization
            // chain doesn't invoke Foo.Singleton.
            foo.Initialize();

            // _singleton remains null until
            // object construction is done.
            _singleton = foo;
          }
      return _singleton;
    }
  }

In Java, utilizza sincronizzato () al posto di blocco (), ma è fondamentalmente la stessa idea. Se c'è la possibile incoerenza di quando il campo viene assegnato Singleton, allora perché non basta usare un primo ambito localmente variabile e quindi assegnare il campo Singleton all'ultimo momento possibile, prima di uscire dalla sezione critica? Mi sto perdendo qualcosa?

C'è l'argomento @ michael-Borgwardt che in C # e Java il campo statico solo si inizializzato una volta al primo utilizzo, ma che comportamento è linguaggio specifico. E ho usato questo modello di frequente per pigri l'inizializzazione di una proprietà della raccolta (ad esempio user.Sessions).

Ho ottenuto ricontrollato blocco a lavorare utilizzando un valore booleano (vale a dire usando un primitivo per evitare l'inizializzazione pigra):

private static Singleton instance;
private static boolean created;
public static Singleton getInstance() {
    if (!created) {
        synchronized (Singleton.class) {
            if (!created) {
                instance = new Singleton();
                created = true;
            }
        }
    }
    return instance;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top