2012-02-10 15 views
9

Ho tipicamente abituato ad attuare un pattern Singleton in questo modo perché è così facile:C'è un problema con questa implementazione singleton?

class MyClass 
{ 
    public: 
     MyClass* GetInstance() 
     { 
      static MyClass instance; 
      return &instance; 
     } 

    private: 
     //Disallow copy construction, copy assignment, and external 
     //default construction. 
}; 

Questo sembra molto più semplice rispetto alla creazione di un puntatore di istanza statica, inizializzandolo nel file di origine, e l'utilizzo di memoria dinamica allocazione nella funzione di istanza con guardie.

C'è un lato negativo che non vedo? Mi sembra thread-safe perché penserei che il primo thread per arrivare alla prima riga provocherebbe l'istanziazione - e sembra carino e conciso. Immagino che ci sia un problema che non vedo visto che non è comune però - vorrei ricevere un feedback prima di continuare ad usarlo

+2

È possibile restituire un MyClass e anziché un MyClass * –

+10

Intendi, a parte il fatto che si tratta di un singleton? http: // StackOverflow.com/q/137975/10077 –

+0

Sì, sì, so che sono un anti-pattern e dovrebbero essere evitati :) Sono ancora interessato al motivo per cui questo potrebbe non essere sicuro per i thread. So che modificare i dati del singleton: i membri dovrebbero essere protetti da mutex, ma ho pensato che la creazione tramite questa funzione sarebbe stata sicura. –

risposta

3

Questa non è una soluzione thread-safe inerente: durante la costruzione dell'istanza, un altro thread può prevenire e tentare di ottenere l'istanza, risultante in una doppia istanza o nell'utilizzo di un'istanza non strutturata.

Questo è gestito da diversi compilatori aggiungendo una guardia (in gcc, penso che ci sia un flag per disabilitarlo) perché non c'è modo di proteggerlo con un mutex definito dall'utente.

+0

Quindi, stai dicendo che anche qui c'è una variabile statica, più di una può essere creata in qualche modo? –

+0

L'istanza viene inizializzata al primo utilizzo della funzione. Suppongo che sia più inizializzato in doppio rispetto alla doppia istanza. – stefaanv

+0

Avrei pensato che il primo thread per colpire la linea con la statica avrebbe attivato la sua creazione/inizializzazione e che il secondo thread che colpiva quella linea sarebbe pienamente consapevole della creazione e dell'inizializzazione a causa di come funzionano le statistiche. Perché non dovrebbe essere così? –

0

Oltre a uno scenario multi-thread - n. Bene - lasciami qualificare, la costruzione è pigra (quindi la prima chiamata potrebbe avere un successo) e la distruzione - beh, non c'è garanzia (a parte questo sarà - ad un certo punto)

3

Lo svantaggio è che tu hai nessun controllo su esattamente quando l'oggetto è distrutto. Questo sarà un problema se altri oggetti statici tentano di accedervi dai loro distruttori.

Un compilatore conforme a C++ 11 deve implementare questo in modo thread-safe; tuttavia, i compilatori più vecchi potrebbero non farlo. In caso di dubbi e in particolare di inizializzazione non voluta, è possibile forzare la creazione dell'oggetto richiamando l'accessor prima di iniziare qualsiasi thread.

+0

Quindi, stai dicendo che anche se c'è una statica in quella funzione, nei compilatori non C++ 11 potrebbe essere doppiamente creata? ma finché chiamo la funzione istanza prima di entrare nella mia fase multi-threaded del codice, si tratta di una creazione thread-safe? solo chiarendo :) Grazie per la risposta finora! –

+0

@ w00te: Sì, è vero. Prima del C++ 11, lo standard non aveva nulla da dire sulla sicurezza dei thread, quindi era interamente compito dell'implementazione del compilatore se proteggere contro la doppia creazione. Dovrebbe essere thread-safe con i compilatori più recenti ragionevolmente. –

+0

+1 Grazie per l'aiuto, è bello sapere che è specifico per la versione standard. –

2

ci sono due questioni nell'interfaccia:

  • Si dovrebbe restituire un riferimento
  • Si dovrebbe fare sia il distruttore o delete operatore private

Inoltre, v'è un lieve rischio di tentare di usare questa classe dopo che è stata distrutta.

Per quanto riguarda i problemi di multi-threading (e l'inizializzazione credo): va bene in C++ 11 e va bene per molto tempo su buoni compilatori C++.

+0

+1 grazie per l'aiuto :) –

0

In generale, il qualificatore static per la variabile locale in un metodo non garantisce che la variabile venga creata una sola volta. Se il metodo è chiamato da diversi thread, potrebbe essere creato una volta per ogni thread tante volte, come molti thread lo hanno chiamato. Non dovrebbe essere confuso con il membro statico della classe, che viene creato una volta prima dell'avvio del programma. La sicurezza del thread delle variabili statiche locali dipende dalla particolare realizzazione di C++. Link utile: Are function static variables thread-safe in GCC?

Spero che aiuti.