A implementação a seguir, usando a inicialização lenta, do Singleton
thread (Meyers 'Singleton) é segura?
static Singleton& instance()
{
static Singleton s;
return s;
}
Caso contrário, por que e como torná-lo seguro?
A implementação a seguir, usando a inicialização lenta, do Singleton
thread (Meyers 'Singleton) é segura?
static Singleton& instance()
{
static Singleton s;
return s;
}
Caso contrário, por que e como torná-lo seguro?
Respostas:
No C ++ 11 , é seguro para threads. De acordo com a norma ,
§6.7 [stmt.dcl] p4
:O suporte do GCC e do VS para o recurso ( Inicialização dinâmica e destruição com simultaneidade , também conhecida como estática mágica no MSDN ) é o seguinte:
Obrigado a @Mankarse e @olen_gam por seus comentários.
No C ++ 03 , esse código não era seguro para threads. Há um artigo de Meyers chamado "C ++ e os perigos do bloqueio com verificação dupla" que discute implementações seguras de threads do padrão, e a conclusão é, mais ou menos, que (no C ++ 03) o bloqueio completo em torno do método de instanciação é basicamente a maneira mais simples de garantir a simultaneidade adequada em todas as plataformas, enquanto a maioria das formas de variantes de padrão de bloqueio com verificação dupla podem sofrer condições de corrida em determinadas arquiteturas , a menos que as instruções sejam intercaladas com estrategicamente coloque barreiras de memória.
fonte
Para responder sua pergunta sobre por que não é seguro para threads, não é porque a primeira chamada para
instance()
deve chamar o construtorSingleton s
. Para ser seguro para threads, isso teria que ocorrer em uma seção crítica, mas não há exigência no padrão de que uma seção crítica seja realizada (o padrão até o momento é completamente silencioso em threads). Os compiladores geralmente implementam isso usando uma verificação e incremento simples de um boolean estático - mas não em uma seção crítica. Algo como o seguinte pseudocódigo:Então, aqui está um simples Singleton seguro para threads (para Windows). Ele usa um invólucro de classe simples para o objeto CRITICAL_SECTION do Windows, para que possamos fazer com que o compilador inicialize automaticamente
CRITICAL_SECTION
antes demain()
ser chamado. Idealmente, seria usada uma verdadeira classe de seção crítica RAII que pode lidar com exceções que podem ocorrer quando a seção crítica é realizada, mas isso está além do escopo desta resposta.A operação fundamental é que, quando uma instância de
Singleton
é solicitada, um bloqueio é realizado, o Singleton é criado, se necessário, o bloqueio é liberado e a referência do Singleton retornada.Cara - isso é muita porcaria para "fazer uma melhor global".
As principais desvantagens dessa implementação (se eu não deixei passar alguns bugs) são:
new Singleton()
jogar, a trava não será liberada. Isso pode ser corrigido usando um objeto de bloqueio RAII verdadeiro, em vez do simples que tenho aqui. Isso também pode ajudar a tornar as coisas portáteis se você usar algo como o Boost para fornecer um invólucro independente da plataforma para o bloqueio.main()
chamada - se você a chamar antes (como na inicialização de um objeto estático), as coisas podem não funcionar porqueCRITICAL_SECTION
podem não ser inicializadas.fonte
new Singleton()
joga?new Singleton()
joga, definitivamente há um problema com a trava. Uma classe de bloqueio RAII adequada deve ser usada, algo como olock_guard
Boost. Queria que o exemplo fosse mais ou menos independente, e já era um monstro, então deixei de lado a segurança de exceção (mas o chamei). Talvez eu deva corrigir isso para que esse código não seja cortado e colado em algum lugar inadequado.Observando o próximo padrão (seção 6.7.4), ele explica como a inicialização local estática é segura para threads. Portanto, uma vez que essa seção do padrão seja amplamente implementada, o Singleton da Meyer será a implementação preferida.
Eu discordo de muitas respostas já. A maioria dos compiladores já implementa a inicialização estática dessa maneira. A única exceção notável é o Microsoft Visual Studio.
fonte
A resposta correta depende do seu compilador. Ele pode decidir torná- lo mais seguro; não é "naturalmente" thread-safe.
fonte
Na maioria das plataformas, isso não é seguro para threads. (Anexe o aviso de isenção de responsabilidade usual, explicando que o padrão C ++ não sabe sobre threads, portanto, legalmente, não diz se é ou não.)
O motivo é que nada impede que mais de um thread execute simultaneamente
s
o construtor."C ++ e os perigos do bloqueio com dupla verificação", de Scott Meyers e Andrei Alexandrescu, é um ótimo tratado sobre o assunto de singletons seguros para threads.
fonte
Como o MSalters disse: Depende da implementação do C ++ usada. Verifique a documentação. Quanto à outra pergunta: "Se não, por quê?" - O padrão C ++ ainda não menciona nada sobre threads. Mas a próxima versão do C ++ está ciente de threads e afirma explicitamente que a inicialização de locais estáticos é segura para threads. Se dois threads chamam essa função, um thread executará uma inicialização enquanto o outro bloqueará e aguardará o término.
fonte