Question

En d’autres termes, ce thread d’implémentation Singleton est-il sécurisé :

public class Singleton
{
    private static Singleton instance;

    private Singleton() { }

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

    public static Singleton Instance
    {
        get { return instance; }
    }
}
Était-ce utile?

La solution

Il est garanti que les constructeurs statiques ne seront exécutés qu'une seule fois par domaine d'application, avant la création d'instances d'une classe ou l'accès à des membres statiques. http://msdn.microsoft.com/en-us/library/aa645612.aspx

L'implémentation présentée est thread-safe pour la construction initiale, c'est-à-dire qu'aucun verrouillage ou test nul n'est requis pour la construction de l'objet Singleton.Toutefois, cela ne signifie pas que toute utilisation de l’instance sera synchronisée.Il existe différentes manières d'y parvenir ;J'en ai montré un ci-dessous.

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();
    }
}

Autres conseils

Bien que toutes ces réponses donnent la même réponse générale, il y a une mise en garde.

N'oubliez pas que toutes les dérivations potentielles d'une classe générique sont compilées sous forme de types individuels.Soyez donc prudent lorsque vous implémentez des constructeurs statiques pour des types génériques.

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

MODIFIER:

Voici la démonstration :

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()));        
    }
}

Dans la console :

Hit System.Object
Hit System.String

Utiliser un constructeur statique en fait est thread-safe.Il est garanti que le constructeur statique ne sera exécuté qu'une seule fois.

À partir de la spécification du langage C# http://msdn.microsoft.com/en-us/library/aa645612(VS.71).aspx:

Le constructeur statique d'une classe s'exécute au plus une fois dans un domaine d'application donné.L'exécution d'un constructeur statique est déclenchée par le premier des événements suivants qui se produisent dans un domaine d'application :

  • Une instance de la classe est créée.
  • Tous les membres statiques de la classe sont référencés.

Alors oui, vous pouvez être sûr que votre singleton sera correctement instancié.

Zooba a fait valoir un excellent argument (et 15 secondes avant moi aussi !) selon lequel le constructeur statique ne garantira pas un accès partagé thread-safe au singleton.Il faudra gérer cela d’une autre manière.

Voici la version Cliffnotes de la page MSDN ci-dessus sur c# singleton :

Utilisez toujours le modèle suivant, vous ne pouvez pas vous tromper :

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

   private Singleton(){}

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

Au-delà des fonctionnalités évidentes du singleton, il vous offre gratuitement ces deux choses (en ce qui concerne le singleton en C++) :

  1. construction paresseuse (ou pas de construction si elle n'a jamais été appelée)
  2. synchronisation

Il est garanti que les constructeurs statiques ne se déclencheront qu'une seule fois par domaine d'application, votre approche devrait donc être correcte.Cependant, sa fonctionnalité n'est pas différente de la version en ligne, plus concise :

private static readonly Singleton instance = new Singleton();

La sécurité des threads est davantage un problème lorsque vous initialisez paresseusement des choses.

Le Spécification de l'infrastructure de langage commun garantit qu’un initialiseur de type doit s’exécuter exactement une fois pour un type donné, à moins qu’il ne soit explicitement appelé par le code utilisateur. (Section 9.5.3.1.) Donc, à moins que vous n’ayez un IL farfelu sur l’appel libre de Singleton ::.cctor directement (peu probable), votre constructeur statique s’exécutera exactement une fois avant que le type Singleton ne soit utilisé, une seule instance de Singleton sera créée, et votre propriété Instance est thread-safe.

Notez que si le constructeur de Singleton accède à la propriété Instance (même indirectement), alors la propriété Instance sera nulle.Le mieux que vous puissiez faire est de détecter quand cela se produit et de lever une exception, en vérifiant que l'instance n'est pas nulle dans l'accesseur de propriété.Une fois votre constructeur statique terminé, la propriété Instance ne sera pas nulle.

Comme La réponse de Zoomba souligne que vous devrez sécuriser l'accès à Singleton à partir de plusieurs threads, ou implémenter un mécanisme de verrouillage autour de l'utilisation de l'instance singleton.

Le constructeur statique va finition en cours d'exécution avant n'importe quel thread est autorisé à accéder à la 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();
    }

Le code ci-dessus a produit les résultats ci-dessous.

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).

Même si l'exécution du constructeur statique a mis beaucoup de temps, les autres threads se sont arrêtés et ont attendu.Tous les threads lisent la valeur de _x définie en bas du constructeur statique.

Juste pour être pédant, mais il n'existe pas de constructeur statique, mais plutôt des initialiseurs de type statique, voici un petit démo de dépendance de constructeur statique cyclique qui illustre ce point.

Le constructeur statique est garanti thread-safe.Consultez également la discussion sur Singleton sur DeveloperZen :http://www.developerzen.com/2007/07/15/whats-wrong-with-this-code-1-discussion/

Bien que les autres réponses soient pour la plupart correctes, il existe encore une autre mise en garde concernant les constructeurs statiques.

Selon la rubrique II.10.5.3.3 Courses et impasses de la ECMA-335 Langage commun Infrastructure

L’initialisation de type seule ne doit pas créer de blocage à moins qu’un code appelé à partir d’un initialiseur de type (directement ou indirectement) explicitement Invoque des opérations de blocage.

Le code suivant entraîne un blocage

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();
    }
}

L'auteur original est Igor Ostrovsky, voir son article ici.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top