Singleton Pattern: The truth behind

Olá galera,

O Singleton é um padrão super popular e muito se deve à sua facilidade de implementação. Entretanto, as implementações que muitos conhecemos e vemos por aí podem ter falhas, principalmente no que diz respeito a sincronismo de Threads. Dependendo do contexto, isso pode se tornar perigoso e quando menos esperamos, o erro está em um Singleton mal implementado. Esse tipo de falha é difícil de identificar em código legado, e aí o padrão simples custa caro.

Vou mostrar algumas formas diferentes de implementar o Singleton, cada uma com seus ganhos e perdas, e como o Rafael Tolotti costuma dizer, “tudo é um trade”. Algumas considerações:

  • Usei sealed em todas as classes para otimizar o JIT, reduzindo custo no dispatching de métodos virtuais (vide artigo sobre Virtual Method Tables e sobre Sealed Classes).
  • Mesmo sem decorar as classes com sealed é importante deixarmos os membros de instância e classe como private. Evita que o princípio do padrão seja quebrado em caso de herança entre classes (sub-classes poderiam instanciar a base-class).

1) Versão “non-thread safe

namespace Singleton.Sample.Implementations
{
    /// <summary>
    /// PS: Keyword "sealed" is used for JIT optimizations
    /// </summary>
    public sealed class Singleton_1_NonThreadSafe
    {
        private static Singleton_1_NonThreadSafe instance = null;

        private Singleton_1_NonThreadSafe() { }

        public static Singleton_1_NonThreadSafe Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new Singleton_1_NonThreadSafe();
                }
                return instance;
            }
        }
    }
}

Comentários: Essa é a versão mais simples e popular do Singleton. Entretanto, se duas ou mais Threads caíssem na linha 16 poderíamos ter uma quebra do princípio e várias instâncias seriam criadas. Abordagem recomendada em cenários simplíssimos, onde por prerrogativa não haverá concorrência no acesso à instância Singleton.

2) Versão “Simple Thread-Safe”

namespace Singleton.Sample.Implementations
{
    /// <summary>
    /// PS: Keyword "sealed" is used for JIT optimizations
    /// PS 2: This class uses a critical zone to solve the concurrent memory-barrier issue.
    /// </summary>
    public sealed class Singleton_2_SimpleThreadSafe
    {
        private static Singleton_2_SimpleThreadSafe instance = null;
        private static readonly object syncRoot = new object();

        Singleton_2_SimpleThreadSafe() { }

        public static Singleton_2_SimpleThreadSafe Instance
        {
            get
            {
                lock (syncRoot)
                {
                    if (instance == null)
                    {
                        instance = new Singleton_2_SimpleThreadSafe();
                    }
                    return instance;
                }
            }
        }
    }
}

Comentários: Versão simples para evitar que duas ou mais threads avaliem a expressão da linha 20 e instanciem o objeto. Para isso é utilizado um objeto imutável de sincronização (cadeado) (criado na linha 10 e trancado na linha 18). Pode apresentar problemas de performance pois um lock é gerenciado toda vez que a instância for acessada.

Obs: Existe uma variação para esta versão que evita que toda vez o lock seja acessado, usando uma condição adicional antes mesmo do lock. Porém ela se torna mais problemática ainda por apresentar brechas em relação ao modelo de gerência de memória do Framework e não sendo tão performática quanto outras implementações.

3) Versão Thread-Safe com Static Constructors e sem cadeados

namespace Singleton.Sample.Implementations
{
    /// <summary>
    /// Static constructor tells the compiler to lazy-load the type-initialization (by NOT marking the class with beforeFieldInit)
    /// and runs only ONCE per AppDomain (what makes it Thread-safe).
    /// </summary>
    public sealed class Singleton_3_ThreadSafeWithoutLock
    {
        private static readonly Singleton_3_ThreadSafeWithoutLock instance = new Singleton_3_ThreadSafeWithoutLock();

        static Singleton_3_ThreadSafeWithoutLock() { }
        private Singleton_3_ThreadSafeWithoutLock() { }

        public Singleton_3_ThreadSafeWithoutLock Instance
        {
            get
            {
                return instance;
            }
        }
    }
}

Comentários: Assim como comentei na classe, o segredo está no Static Constructor. Ano passado eu fiz um post sobre Static Constructors, sugiro que seja lido para melhor entendimento. O Static Constructor vai garantir que a inicialização dos tipos (nesta versão é in-line, de acordo com [09]) seja executada apenas uma vez por AppDomain, e também que as variáveis sejam inicializadas quando o primeiro membro estático da classe for acessado, o que de certa forma, garante o fake-lazy-load (digo “fake” porque se nenhum membro estático for acessado os type-initializers não executarão). Além do ganho de performance com o lazy-loading, o código fica bem “clean” e a menos que queiramos fazer um eager-loading (inicializando a instância dentro do Static Constructor), o Static Constructor deverá ficar vazio.

Clique aqui para baixar a solução de código usada no post.

Galera por hoje é isso vou tentar não sumir tanto do blog hehe.
Abraço!

Static Constructors – Mito ou realidade?

Se tem uma coisa que as pessoas que trabalham com C# pouco conhecem e utilizam, podem apostar, são Static Constructors.

E sinceramente, eu demorei bastante para saber de sua existência. Estava pesquisando sobre corretas implementações do Singleton Pattern e em uma delas, me flagrei necessitado a usar um Static Constructor.

Portanto, antes de falar sobre Singleton aqui, achei melhor dedicar um post exclusivo para os construtores estáticos.

Em sua essência, Static Constructors têm como principal objetivo inicializar os membros estáticos de uma classe antes que:

  • Qualquer instância desta classe seja criada; ou
  • Qualquer membro estático seja referenciado dentro do application domain.

Isso nos dá um recurso poderoso de inicialização centralizada dos membros estáticos ou uma opção caso queiramos executar alguma ação de setup da classe apenas uma vez por AppDomain (o que pode garantir thread-safe nas soluções em que for usado).

Mas para alguns isso pode soar estranho, afinal C#, assim como a maioria das linguagens OOP atuais, possui o recurso de type initialization. Deste modo, é comum encontrarmos trechos de código como:

class Program
{
       static int count = 1000
}

Porém, é possível ainda que a mesma variável count seja inicializada desta forma:

class Program
{
       static int count;
       static Program()
       {
              count = 1000;
       }
}

De acordo com a especificação do C# (ECMA 334), existem algumas regras básicas para usar Static Constructors:

  • Só pode existir um StaticConstructor por classe.
  • O Static Constructor não pode possuir parâmetros.
  • O Static Constructor somente pode acessar membros estáticos da classe.
  • O Static Constructor não pode ter modificador de acesso.

Superadas essas limitações, por que utilizar Static Constructors e não type-initializers?

Vamos supor que na classe Program exista um método que utilize a variável estática count.

Ao NÃO escrevermos um construtor estático, o compilador do C# marcará a classe com o atributo beforeFieldInit.

E o que isso quer dizer?

Conforme o ECMA do CLI, beforeFieldInit specifies that calling static methods of the type does not force the system to initialize the type.”.

Em outras palavras, não é garantido a inicialização da variável ao acessar o método dependente, e sim ao primeiro acesso de uma variável do mesmo tipo (int no caso).

Se a classe possuir construtor estático, o compilador não decorará a classe com beforeFieldInit, nos garantindo que as variáveis estejam inicializadas:

  • Antes do primeiro acesso a qualquer campo estático ou de instância; ou
  • Antes da primeira invocação para qualquer método estático, de instância ou virtual.

Isso soluciona alguns problemas de inicialização de recursos, incluindo os campos estáticos utilizados na sincronização de Singleton Patterns (que veremos no próximo post!)

Além de centralizar a inicialização de membros estáticos, os construtores estáticos nos garantem a inicialização dos tipos antes de qualquer utilização dependente posterior, evitando exceções com type-initializers.

Até a próxima, espero poder ter tempo pra postar em breve!

foreach vs List.ForEach – Otimização de código

Escrever código otimizado é uma boa prática? Óbviamente, isso faz parte de toda cartilha de desenvolvimento, está sempre sendo falado, etc etc etc..

Mas O.k, agora defina otimização.

Ainda essa semana uma questão parecida surgiu no trabalho e me senti tentado à relatá-la.

Para quem não sabe, com o advento do .NET 3.5 a classe List(T) ganhou o método ForEach que recebe como parâmetro a delegate Action(T). Esse método, segundo a própria documentação do Framework, executa uma determinada ação para cada elemento da lista.

Desta forma, além de percorrer os itens de uma List(T) da forma já conhecida:

IList<string> names = new List<string>()
{
    "John Locke",
    "Mary",
    "James Bond",
    "Sam"
};

foreach (string name in names)
{
    Console.WriteLine(name);
}

Podemos usar:

names.ForEach(name => Console.WriteLine(name));

Ou ainda (para quem não quiser a expressão lambda ou precisar usar mais de uma expressão por iteração):

names.ForEach(delegate(string name) { Console.WriteLine(name); });

Mas o que muita gente não sabe, e aqui entra a discussão, é que para algumas operações (e.g: soma total dos números de uma lista) este método ForEach mostrou-se ser mais otimizado EM TERMOS de tempo de resposta.

Sugiro o post do Dustin Campbell (MVP C#) para quem quiser dar uma olhada nos números e curvas de performance das várias formas de percorrer os itens de uma lista (e no exemplo, somá-los) utilizando duas configurações no compilador: não otimizar geração de código (e.g: Debug) e otimizar geração de código (e.g: Release). Ele ainda disponibilizou os códigos de teste para quem quiser baixar e conferir.

Também sugiro que, assim como eu, quem se interessar implemente seus próprios testes para fixar as diferentes sintaxes 🙂

Mas e se meu critério de otimização não for tempo de resposta? For legibilidade ou produtividade ao manter contato com o código?

Há quem defenda as formas tradicionais (as quais grande parte dos desenvolvedores .NET conhecem) e acha que essa nova “sintaxe” ou forma de iterar não adiciona nenhum “poder representativo à linguagem”. Podemos conferir alguns motivos para não usar o List(T).ForEach no blog do Eric Lippert.

Por outro lado, existem pessoas que defendem que essa nova forma adiciona um poder de leitura ao código orientado à fluência, da esquerda pra direita. (Particularmente, mais do que a performance, esse argumento me agrada). Neste caso, sugiro a leitura do Coffee Driven Development.

Ainda assim, fui questionado por um colega sobre como interromper uma iteração com .ForEach e, com um pouco de procura a “solução” mais comum encontrada foi utilizar “return;” dentro do delegate passado para o .ForEach.

Porém, acredito que a melhor forma de usá-lo seria para expressões simples, como o Console.WriteLine acima, usufruindo de uma melhor performance e não comprometendo a legibilidade do código.

O legal é discutir com diferentes pontos de vista e chegar a um consenso que agregue valor ao software da forma como for mais prioritária: será que vale mais a pena otimizar a resposta de uma iteração? otimizar uma boa leitura e entendimento para outros desenvolvedores? otimizar.. otimizar…