Aşağıdaki uygulama, Singleton
(Meyers 'Singleton) ipliğinin tembel başlatılmasını kullanarak güvenli midir?
static Singleton& instance()
{
static Singleton s;
return s;
}
Değilse, neden ve nasıl iplik güvenli hale getirilir?
Aşağıdaki uygulama, Singleton
(Meyers 'Singleton) ipliğinin tembel başlatılmasını kullanarak güvenli midir?
static Singleton& instance()
{
static Singleton s;
return s;
}
Değilse, neden ve nasıl iplik güvenli hale getirilir?
Yanıtlar:
In C ++ 11 , bu iş parçacığı güvenlidir. Göre standart , §6.7 [stmt.dcl] p4
:
Değişken başlatılırken kontrol bildirime aynı anda girerse, eşzamanlı yürütme başlatmanın tamamlanmasını bekleyecektir .
Özellik için GCC ve VS desteği ( MSDN'de Sihirli Statik olarak da bilinen Eşzamanlılık ile Dinamik Başlatma ve İmha ) aşağıdaki gibidir:
@Mankarse ve @olen_gam'a yorumları için teşekkürler.
In C ++ 03 , bu kod parçacığı güvenli değildi. Meyers tarafından "C ++ ve Çift Kontrollü Kilitlemenin Tehlikeleri" adlı bir makale vardır, bu da desenin iş parçacığı güvenli uygulamalarını tartışır ve sonuç, az çok, (C ++ 03'te) örnekleme yönteminin etrafında tam kilitlemedir. temel olarak tüm platformlarda uygun eşzamanlılığı sağlamanın en basit yoludur, ancak çift denetimli kilitleme deseni varyantlarının çoğu biçimi, stratejik olarak yerleştirilen bellek engelleriyle birlikte yerleştirilmedikçe belirli mimarilerdeki yarış koşullarından muzdarip olabilir .
Neden threadsafe olmadığına dair sorunuzu cevaplamak için, ilk çağrının yapıcıyı çağırması instance()
gerektiği için değil Singleton s
. İş parçacığı güvenli olmak için bu kritik bir bölümde gerçekleşmelidir ve standartta kritik bir bölümün alınmasına gerek yoktur (bugüne kadar standart dişler üzerinde tamamen sessizdir). Derleyiciler bunu genellikle statik bir booleanın basit bir kontrolünü ve artışını kullanarak uygular - ancak kritik bir bölümde değil. Aşağıdaki sözde kod gibi bir şey:
static Singleton& instance()
{
static bool initialized = false;
static char s[sizeof( Singleton)];
if (!initialized) {
initialized = true;
new( &s) Singleton(); // call placement new on s to construct it
}
return (*(reinterpret_cast<Singleton*>( &s)));
}
İşte basit bir iş parçacığı için güvenli Singleton (Windows için). Biz derleyici otomatik olarak başlatmak zorunda böylece, Windows CRITICAL_SECTION nesnesi için basit bir sınıf sarmalayıcı kullanır CRITICAL_SECTION
önce main()
denir. İdeal olarak, kritik bölüm tutulduğunda ortaya çıkabilecek istisnalarla başa çıkabilen gerçek bir RAII kritik bölüm sınıfı kullanılır, ancak bu, bu cevabın kapsamı dışındadır.
Temel işlem, bir örneği Singleton
istendiğinde bir kilit alındığında, gerekiyorsa Singleton'ın oluşturulması, daha sonra kilidin serbest bırakılması ve Singleton referansının geri döndürülmesidir.
#include <windows.h>
class CritSection : public CRITICAL_SECTION
{
public:
CritSection() {
InitializeCriticalSection( this);
}
~CritSection() {
DeleteCriticalSection( this);
}
private:
// disable copy and assignment of CritSection
CritSection( CritSection const&);
CritSection& operator=( CritSection const&);
};
class Singleton
{
public:
static Singleton& instance();
private:
// don't allow public construct/destruct
Singleton();
~Singleton();
// disable copy & assignment
Singleton( Singleton const&);
Singleton& operator=( Singleton const&);
static CritSection instance_lock;
};
CritSection Singleton::instance_lock; // definition for Singleton's lock
// it's initialized before main() is called
Singleton::Singleton()
{
}
Singleton& Singleton::instance()
{
// check to see if we need to create the Singleton
EnterCriticalSection( &instance_lock);
static Singleton s;
LeaveCriticalSection( &instance_lock);
return s;
}
Adam - "daha iyi bir küresel olmak" için çok saçma.
(Bazı hataların geçmesine izin vermediysem) bu uygulamanın ana dezavantajları:
new Singleton()
atar, kilit serbest olmayacak. Bu, burada sahip olduğum basit yerine gerçek bir RAII kilit nesnesi kullanılarak düzeltilebilir. Bu, kilit için platformdan bağımsız bir sargı sağlamak için Boost gibi bir şey kullanırsanız, işleri taşınabilir hale getirmeye de yardımcı olabilir.main()
çağrıldıktan sonra iş parçacığı güvenliğini garanti eder - daha önce çağırırsanız (statik nesnenin başlatılmasında olduğu gibi), başlatılmayabileceğinden işler çalışmayabilir CRITICAL_SECTION
.new Singleton()
Atarsa ne olur ?
new Singleton()
atarsa, kilitte kesinlikle bir sorun vardır. lock_guard
Boost gibi bir şey uygun bir RAII kilit sınıfı kullanılmalıdır . Örneğin az ya da çok bağımsız olmasını istedim ve zaten bir canavar gibiydi, bu yüzden istisna güvenliğini bıraktım (ama çağırdım). Belki bu kod bu yüzden uygunsuz bir yere cut-n-yapıştırılan almaz bunu düzeltmek gerekir.
Bir sonraki standarda (bölüm 6.7.4) bakıldığında, statik lokal başlatmanın iş parçacığı açısından güvenli olduğunu açıklar. Dolayısıyla, standardın bu bölümü yaygın olarak uygulandığında, Meyer'in Singleton'u tercih edilen uygulama olacaktır.
Zaten birçok cevaba katılmıyorum. Çoğu derleyici bu şekilde statik başlatmayı zaten uygular. Dikkate değer bir istisna Microsoft Visual Studio'dur.
Aşağıdaki uygulama [...] iş parçacığı güvenli midir?
Çoğu platformda, bu iş parçacığı için güvenli değildir. (C ++ standardının iş parçacıkları hakkında bilgi sahibi olmadığını açıklayan olağan feragatnameyi ekleyin, bu nedenle, yasal olarak, olup olmadığını söylemez.)
Değilse, neden [...]?
Bunun nedeni, hiçbir şeyin birden fazla iş parçacığının aynı anda s
yapıcı çalıştırmasını engellememesidir .
nasıl iplik güvenli hale getirmek için?
Scott Meyers ve Andrei Alexandrescu'nun "C ++ ve Çift Kontrollü Kilitlemenin Tehlikeleri" , iplik korumalı singletonlar konusunda oldukça iyi bir incelemedir.
MSalters'ın dediği gibi: Kullandığınız C ++ uygulamasına bağlıdır. Dokümanları kontrol edin. Diğer soruya gelince: "Değilse, neden?" - C ++ standardı henüz dişler hakkında bir şey söylemiyor. Ancak yaklaşan C ++ sürümü iş parçacıklarının farkındadır ve statik yerel ayarların başlatılmasının iş parçacığı açısından güvenli olduğunu açıkça belirtir. İki iş parçacığı böyle bir işlevi çağırırsa, bir iş parçacığı bir başlatma işlemi gerçekleştirir, diğer iş parçacığı bunu engeller ve bitmesini bekler.