Domanda

Ricordo di aver letto che le variabili statiche dichiarate all'interno dei metodi non sono thread-safe. (Vedi Che dire del singleton di Meyer? come menzionato da Todd Gardner )

Dog* MyClass::BadMethod()
{
  static Dog dog("Lassie");
  return &dog;
}

La mia libreria genera codice C ++ che gli utenti finali possono compilare come parte della loro applicazione. Il codice che genera deve inizializzare le variabili statiche in modo multipiattaforma sicuro per i thread. Vorrei usare boost::call_once per silenziare l'inizializzazione della variabile, ma poi gli utenti finali sono esposti alla dipendenza Boost.

Esiste un modo per farlo senza forzare dipendenze extra dagli utenti finali?

È stato utile?

Soluzione

Hai ragione nel dire che l'inizializzazione statica del genere non è thread-safe ( qui è un articolo che discute in cosa lo trasformerà il compilatore)

Al momento, non esiste un modo standard, sicuro per i thread e portatile per inizializzare i singoli statici. È possibile utilizzare il blocco con doppio controllo, ma sono necessarie librerie di threading potenzialmente non portatili (vedere una discussione qui ).

Ecco alcune opzioni se la sicurezza del thread è un must:

  1. Non essere pigro (caricato): inizializza durante l'inizializzazione statica. Potrebbe essere un problema se un'altra statica chiama questa funzione nel suo costruttore, poiché l'ordine di inizializzazione statica non è definito (vedi qui ).
  2. Usa boost (come hai detto) o Loki
  3. Rotoli il tuo proprio singleton sulle tue piattaforme supportate (probabilmente dovrebbe essere evitato a meno che sei un esperto di threading)
  4. Blocca un mutex ogni volta che ti serve l'accesso. Questo potrebbe essere molto lento.

Esempio per 1:

// in a cpp:
namespace {
    Dog dog("Lassie");
}

Dog* MyClass::BadMethod()
{
  return &dog;
}

Esempio per 4:

Dog* MyClass::BadMethod()
{
  static scoped_ptr<Dog> pdog;
  {
     Lock l(Mutex);
     if(!pdog.get())
       pdog.reset(new Dog("Lassie"));
  }
  return pdog.get();
}

Altri suggerimenti

Non sono sicuro se questo è ciò che intendi o meno, ma puoi rimuovere la dipendenza boost dai sistemi POSIX chiamando invece pthread_once. Immagino che dovresti fare qualcosa di diverso su Windows, ma evitare questo è esattamente il motivo per cui boost ha in primo luogo una libreria di thread e perché le persone pagano il prezzo di dipendere da esso.

Fare qualsiasi cosa " thread-safe " è intrinsecamente legato all'implementazione dei thread. Devi dipendere da qualcosa , anche se è solo il modello di memoria dipendente dalla piattaforma. Semplicemente non è possibile nel puro C ++ 03 assumere nulla dei thread, che sono al di fuori dell'ambito del linguaggio.

Un modo in cui potresti farlo che non richiede un mutex per la sicurezza del thread è rendere il singleton un file statico, piuttosto che una funzione statica:

static Dog dog("Lassie");
Dog* MyClass::BadMethod()
{
  return &dog;
}

L'istanza Dog verrà inizializzata prima dell'esecuzione del thread principale. Le variabili statiche dei file hanno un famoso problema con l'ordine di inizializzazione, ma fintanto che il Dog non si affida a nessun altro statico definito in un'altra unità di traduzione, questo non dovrebbe essere un problema.

L'unico modo che conosco per garantire che non avrai problemi di threading con risorse non protette come la tua "static Dog" è di rendere obbligatoria l'istanza prima di qualsiasi thread vengono creati.

Potrebbe essere semplice come documentare che devono chiamare una funzione MyInit() nel thread principale prima di fare qualsiasi altra cosa. Quindi costruisci <=> per creare un'istanza e distruggere un oggetto di ciascun tipo che contiene una di quelle statiche.

L'unica altra alternativa è quella di mettere un'altra limitazione su come possono usare il tuo codice generato (usa Boost, thread Win32, ecc.). Entrambe queste soluzioni sono accettabili secondo me: va bene generare regole che devono seguire.

Se non seguono le regole stabilite dalla documentazione, tutte le scommesse sono disattivate. La regola che devono chiamare una funzione di inizializzazione o dipendere da Boost non è irragionevole per me.

AFAIK, l'unica volta che è stato fatto in modo sicuro e senza mutex o prima inizializzazione di istanze globali è nel < em> C ++ imperfetto , che discute come farlo usando un " spin mutex " ;. Non sono vicino alla mia copia, quindi non posso dirtelo più precisamente in questo momento.

IIRC, ci sono alcuni esempi dell'uso di questo all'interno delle STLSoft , anche se posso ' ricorda quali componenti in questo momento.

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