Din păcate, răspunsul lui Matt conține ceea ce se numește blocare dublă verificată care nu este acceptată de modelul de memorie C / C ++. (Aceasta este susținută de Java 1.5 și mai târziu și cred că .NET model de memorie.) Aceasta înseamnă că între momentul în care verificarea pObj == NULL
are loc și când blocarea (mutex) este dobândită, pObj
poate să fi fost deja atribuită unui alt fir. Comutarea între ele se face ori de câte ori OS-ul dorește, nu între "liniile" unui program (care nu au semnificație post-compilație în majoritatea limbilor).
În plus, după cum recunoaște Matt, el folosește un int
ca un element de blocare mai degrabă decât un primitiv sistem de operare. Nu face asta. Încuietori adecvate necesită utilizarea instrucțiunilor de barieră de memorie, potențiale șocuri de linie cache și așa mai departe; utilizați primitivele sistemului de operare pentru blocare. Acest lucru este deosebit de important deoarece primitivele utilizate pot schimba între liniile CPU individuale pe care rulează sistemul de operare; ce funcționează pe un procesor Foo ar putea să nu funcționeze pe CPU Foo2. Majoritatea sistemelor de operare suportă fire native POSIX (pthreads) sau le oferă ca ambalaj pentru pachetul de filetare OS, deci este adesea mai bine să ilustrați exemplele care le utilizează.
Dacă sistemul dvs. de operare oferă primitive adecvate și dacă aveți absolut nevoie de performanță, în loc să efectuați acest tip de blocare / inițializare, puteți utiliza o operație comparare atomică și swap pentru a inițializa o variabilă globală comună. În esență, ceea ce scrieți va arăta astfel:
MySingleton *MySingleton::GetSingleton() {
if (pObj == NULL) {
// create a temporary instance of the singleton
MySingleton *temp = new MySingleton();
if (OSAtomicCompareAndSwapPtrBarrier(NULL, temp, &pObj) == false) {
// if the swap didn't take place, delete the temporary instance
delete temp;
}
}
return pObj;
}
Acest lucru funcționează numai dacă este sigur să creați mai multe instanțe ale singleton-ului dvs. (una pentru fiecare fir care se întâmplă să invocați simultan GetSingleton ()) și apoi aruncați extras. Funcția OSAtomicCompareAndSwapPtrBarrier
furnizată pe Mac OS X? majoritatea sistemelor de operare oferă un primitiv similar? verifică dacă pObj
este NULL
și o va seta numai la temp
dacă este. Acest lucru utilizează suportul hardware pentru a într-adevăr, literalmente efectua doar swap o dată și spune dacă sa întâmplat.
O altă posibilitate de a obține un efect de levier în cazul în care sistemul dvs. de operare oferă acest lucru între aceste două extreme este pthread_once
. Aceasta vă permite să configurați o funcție care se execută o singură dată - practic făcând toate blocările / bariera / etc. trickery pentru tine - indiferent de câte ori este invocat sau de câte fire sunt invocate.