public static ControladorUsuariosPorArea ObterInstancia() { // Aqui vem o segundo segredo da implementação, o Singleton tem o benefício de carregamento preguiçoso, // ou seja diferente das variáveis globais o singleton só cria a instancia quando é solicitado a primeira vez // só que em aplicações multi thread a solicitação da instancia inicial pode ocorrer em paralelo. // Então sem blocar o código verificamos se a instancia ainda está nula. Depois da 1 criação nunca vai entrar // nesse IF, mas sabemos que a primeira criação pode estar ocorrendo em paralelo. if (_instanciaUnica == null) { // Após entrar no if ai sim bloqueamos o objeto, para garantir a concorrência nesse trecho do código, ao invéz do paralelismo // ATENÇÃO!! RETIRE OS COMENTÁRIOS DA LINHA ABAIXO PARA VER O PROBLEMA SER RESOLVIDO //lock (_bloqueioSingleton) //{ // Aqui fazemos a mesma verificação de novo, ai mesmo que passamos do primeiro if no caso da criação inicial paralela // não passaremos desse segundo, e não geraremos o problema if (_instanciaUnica == null) { _instanciaUnica = new ControladorUsuariosPorArea(); } //} } return(_instanciaUnica); }
public void Executar() { var threads = new Thread[20]; for (int i = 0; i < threads.Length; i++) { var i1 = i; threads[i] = new Thread(new ThreadStart(() => ControladorUsuariosPorArea.ObterInstancia().TentarAdicionarUsuarioArea($"João {i1}"))); } foreach (var t in threads) { t.Start(); } foreach (var t in threads) { t.Join(); } ControladorUsuariosPorArea.ObterInstancia().ImprimirUsuarioNaArea(); }