Pergunta

Em outras palavras, este thread de implementação do Singleton é seguro:

public class Singleton
{
    private static Singleton instance;

    private Singleton() { }

    static Singleton()
    {
        instance = new Singleton();
    }

    public static Singleton Instance
    {
        get { return instance; }
    }
}
Foi útil?

Solução

É garantido que os construtores estáticos sejam executados apenas uma vez por domínio de aplicativo, antes que qualquer instância de uma classe seja criada ou que qualquer membro estático seja acessado. http://msdn.microsoft.com/en-us/library/aa645612.aspx

A implementação mostrada é thread-safe para a construção inicial, ou seja, nenhum bloqueio ou teste nulo é necessário para construir o objeto Singleton.No entanto, isso não significa que qualquer uso da instância será sincronizado.Existem várias maneiras de fazer isso;Eu mostrei um abaixo.

public class Singleton
{
    private static Singleton instance;
    // Added a static mutex for synchronising use of instance.
    private static System.Threading.Mutex mutex;
    private Singleton() { }
    static Singleton()
    {
        instance = new Singleton();
        mutex = new System.Threading.Mutex();
    }

    public static Singleton Acquire()
    {
        mutex.WaitOne();
        return instance;
    }

    // Each call to Acquire() requires a call to Release()
    public static void Release()
    {
        mutex.ReleaseMutex();
    }
}

Outras dicas

Embora todas essas respostas forneçam a mesma resposta geral, há uma ressalva.

Lembre-se de que todas as derivações potenciais de uma classe genérica são compiladas como tipos individuais.Portanto, tenha cuidado ao implementar construtores estáticos para tipos genéricos.

class MyObject<T>
{
    static MyObject() 
    {
       //this code will get executed for each T.
    }
}

EDITAR:

Aqui está a demonstração:

static void Main(string[] args)
{
    var obj = new Foo<object>();
    var obj2 = new Foo<string>();
}

public class Foo<T>
{
    static Foo()
    {
         System.Diagnostics.Debug.WriteLine(String.Format("Hit {0}", typeof(T).ToString()));        
    }
}

Na consola:

Hit System.Object
Hit System.String

Usando um construtor estático, na verdade é discussão segura.O construtor estático tem garantia de ser executado apenas uma vez.

Da especificação da linguagem C# http://msdn.microsoft.com/en-us/library/aa645612(VS.71).aspx:

O construtor estático de uma classe é executado no máximo uma vez em um determinado domínio de aplicação.A execução de um construtor estático é acionada pelo primeiro dos seguintes eventos que ocorre dentro de um domínio de aplicativo:

  • Uma instância da classe é criada.
  • Qualquer um dos membros estáticos da classe é referenciado.

Então, sim, você pode confiar que seu singleton será instanciado corretamente.

Zooba fez uma observação excelente (e 15 segundos antes de mim também!) de que o construtor estático não garantirá acesso compartilhado seguro para threads ao singleton.Isso precisará ser tratado de outra maneira.

Aqui está a versão Cliffnotes da página MSDN acima em c# singleton:

Use o seguinte padrão, sempre, você não pode errar:

public sealed class Singleton
{
   private static readonly Singleton instance = new Singleton();

   private Singleton(){}

   public static Singleton Instance
   {
      get 
      {
         return instance; 
      }
   }
}

Além dos recursos óbvios do singleton, ele oferece essas duas coisas gratuitamente (em relação ao singleton em c++):

  1. construção preguiçosa (ou nenhuma construção se nunca foi chamada)
  2. sincronização

É garantido que os construtores estáticos sejam acionados apenas uma vez por domínio do aplicativo, portanto, sua abordagem deve ser correta.No entanto, funcionalmente não é diferente da versão in-line mais concisa:

private static readonly Singleton instance = new Singleton();

A segurança do thread é mais um problema quando você inicializa as coisas preguiçosamente.

O Especificação da infraestrutura de linguagem comum Garanta que "um inicializador de tipo deve ser executado exatamente uma vez para qualquer tipo, a menos que explicitamente chamado pelo código do usuário". (Seção 9.5.3.1.) Portanto, a menos que você tenha algum pouco de Il no Soltleton Singleton ::. e sua propriedade de instância é segura por threads.

Observe que se o construtor do Singleton acessar a propriedade Instance (mesmo que indiretamente), então a propriedade Instance será nula.O melhor que você pode fazer é detectar quando isso acontece e lançar uma exceção, verificando se a instância não é nula no acessador de propriedade.Depois que seu construtor estático for concluído, a propriedade Instance não será nula.

Como A resposta de Zoomba aponta que você precisará tornar o Singleton seguro para acesso a partir de vários threads ou implementar um mecanismo de bloqueio usando a instância do singleton.

O construtor estático irá terminar correndo antes qualquer thread tem permissão para acessar a classe.

    private class InitializerTest
    {
        static private int _x;
        static public string Status()
        {
            return "_x = " + _x;
        }
        static InitializerTest()
        {
            System.Diagnostics.Debug.WriteLine("InitializerTest() starting.");
            _x = 1;
            Thread.Sleep(3000);
            _x = 2;
            System.Diagnostics.Debug.WriteLine("InitializerTest() finished.");
        }
    }

    private void ClassInitializerInThread()
    {
        System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() starting.");
        string status = InitializerTest.Status();
        System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() status = " + status);
    }

    private void classInitializerButton_Click(object sender, EventArgs e)
    {
        new Thread(ClassInitializerInThread).Start();
        new Thread(ClassInitializerInThread).Start();
        new Thread(ClassInitializerInThread).Start();
    }

O código acima produziu os resultados abaixo.

10: ClassInitializerInThread() starting.
11: ClassInitializerInThread() starting.
12: ClassInitializerInThread() starting.
InitializerTest() starting.
InitializerTest() finished.
11: ClassInitializerInThread() status = _x = 2
The thread 0x2650 has exited with code 0 (0x0).
10: ClassInitializerInThread() status = _x = 2
The thread 0x1f50 has exited with code 0 (0x0).
12: ClassInitializerInThread() status = _x = 2
The thread 0x73c has exited with code 0 (0x0).

Embora o construtor estático demorasse muito para ser executado, os outros threads pararam e esperaram.Todos os threads leem o valor de _x definido na parte inferior do construtor estático.

Só para ser pedante, mas não existe um construtor estático, mas sim inicializadores de tipo estático, aqui está um pequeno demonstração da dependência do construtor estático cíclico que ilustra este ponto.

O construtor estático tem garantia de segurança de thread.Além disso, confira a discussão sobre Singleton no DeveloperZen:http://www.developerzen.com/2007/07/15/whats-wrong-with-this-code-1-discussion/

Embora outras respostas estejam em sua maioria corretas, há ainda outra ressalva com os construtores estáticos.

Conforme seção II.10.5.3.3 Corridas e impasses do ECMA-335 Infraestrutura de linguagem comum

A inicialização do tipo sozinha não deve criar um impasse, a menos que algum código chamado de um inicializador de tipo (direta ou indiretamente) invoce explicitamente as operações de bloqueio.

O código a seguir resulta em um impasse

using System.Threading;
class MyClass
{
    static void Main() { /* Won’t run... the static constructor deadlocks */  }

    static MyClass()
    {
        Thread thread = new Thread(arg => { });
        thread.Start();
        thread.Join();
    }
}

O autor original é Igor Ostrovsky, veja sua postagem aqui.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top