420

Forcément, tu n'as même pas mis de double-checked lock tongue (Ça à toujours été valide en .NET sur x86 contrairement à Java, mais le modèle mémoire de .NET à été un peu renforcé pour que ça marche partout ailleurs)
Mais en C#, le mieux reste
class Truc
{
    public static readonly Truc = new Truc();

    private Truc() { }
}
Pour les classes légères. (Pour les classes "lourdes", il vaut sans doute mieux inclure un constructeur statique vide)
Bien sûr, s'il y a d'autres membres statiques à initialiser il faut adapter. (Par exemple placer le singleton dans sa propre classe privée imbriquée)
Tu as une garantie forte offerte par le CLR là dessus (initialisation unique, sauf exception déclenchée dans le constructeur statique), et c'est potentiellement plus efficace.


Sinon, tu as aussi Lazy<T> en 4.0, ça fonctionne un peu différemment, mais je n'en ai jamais eu l'utilité, donc je ne peux pas en dire plus tongue
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

421

Brunni (./419) :
... en faisant 2 accès successifs d'un même thread dans mon appli Métro \o/
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

422

Oui et ? tongue
Le bug que tu rencontre sans protéger tes singletons c'est pas simplement d'avoir deux valeurs sur deux threads différents…
Si le thread 1 initialise le singleton pendant que le thread 2 lit le null, puis que le thread 2 stocke le singleton juste après que le thread 1 ait retourné le singleton, le thread 1 a son singleton a lui, et la fois suivante (qui peut être « juste après ») il récupèrera (très probablement) le singleton créé par le thread 2. Donc le thread 1 a deux valeurs différentes l'une à la suite de l'autre.

Certes, Brunni n'a pas précisé si y'a un autre thread qui bidouille derrière, mais c'est le bug « courant », et ça semble l'explication la plus logique à ce problème sans plus de précision…


Quoi qu'il en soit, le mieux à utiliser reste le singleton « naturel » de C#, qui ne foire « jamais » ^^ (Et ne nécessite qu'une ligne de code tongue)
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

423

De ce que j'ai compris de son post il n'y a qu'un seul thread, puisqu'il l'a précisé c'est qu'il est conscient de ce que ça aurait provoqué en multi-thread, et donc j'imagine qu'il aurait trouvé l'erreur tout seul si c'était réellement ça. Mais bon on verra bien.
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

424

Sisi en fait il y avait un autre thread qui bidouillait en même temps. Il se trouve qu'une tâche périodique était lancée en background à ce moment là et se retrouvait accéder au singleton pendant que mon thread principal l'initialisait. Le bug classique en effet (pour ma défense c'est un collègue qui l'a codée cette tâche tongue). Bref au final je vais remplacer par ça, du moins pour les grosses classes.
        #region Singleton boilerplate code (v2)
        private static readonly Lazy<ClientLibrary> instance = new Lazy<ClientLibrary>(() => new ClientLibrary());

        public static ClientLibrary Instance
        {
            get { return instance.Value; }
        }

        private ClientLibrary() { }
        #endregion
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

425

Bon, alors honte sur toi et bien vu GC hehe
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

426

Pourquoi pas juste ça ? cheeky
        private static readonly ClientLibrary instance = new ClientLibrary();

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

        static ClientLibrary() { }

        private ClientLibrary() { }
Ça devrait économiser quelques octets de mémoire, et peut-être quelques cycles à chaque accès. (J'en suis presque certain, j'ai juste la flemme de faire les tests tongue)

Zeph > J'aurai quand même été fort surpris que ce soit un bug du runtime .NET que personne n'ait jamais détecté… Mais j'avoue que tu m'as fait douter à un moment grin
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

427

Bah ça paraissait gros, mais comme j'avais compris de son post qu'il avait déjà éliminé la possibilité d'une race condition... ^^

(pourquoi le constructeur statique vide ?)
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

428

GoldenCrystal (./426) :
Ça devrait économiser quelques octets de mémoire, et peut-être quelques cycles à chaque accès. (J'en suis presque certain, j'ai juste la flemme de faire les tests tongue.gif )

Bon écoute Golden, si vraiment tu veux progresser en programmation, arrête de compter les cycles et les octets pour les langages de haut niveau, tu m'as compris ?

429

grin

Ceci dit, contrairement à ce que certains pensent (ou apprennent), il est souvent important de savoir comment c'est géré derrière ; sinon tu finis toujours soit par pondre un code qui va ramer monstrueusement, avoir des fuites mémoire, ou qui plantera sans que tu comprennes pourquoi.
avatar
<<< Kernel Extremis©®™ >>> et Inventeur de la différence administratif/judiciaire ! (©Yoshi Noir)

<Vertyos> un poil plus mais elle suce bien quand même la mienne ^^
<Sabrina`> tinkiete flan c juste qu'ils sont jaloux que je te trouve aussi appétissant

430

Brunni (./419) :
J'ai réussi à faire un singleton où j'obtiens deux instances différentes en faisant 2 accès successifs d'un même thread dans mon appli Métro \o/
        public static TrucMuche Instance
        {
            get
            {
                if (sharedInstance == null)
                    sharedInstance = new TrucMuche();
                return sharedInstance;
            }
        }

Dieu sait la merde qui s'est passé derrière grin

Bah, je ne connais pas le C#, mais personnellement je ferais ça (en C++/Qt):
  private static QAtomicPointer<TrucMuche> sharedInstance;
  private static QMutex sharedInstanceMutex;
  public static TrucMuche *getInstance() {
    // fast lock-free path for the common case, using atomicity
    if (sharedInstance != 0)
      return sharedInstance;
    {
      QMutexLocker lock(&sharedInstanceMutex);
      if (sharedInstance != 0)
        return sharedInstance;
      TrucMuche *newInstance = new TrucMuche();
      sharedInstance = newInstance;
      return newInstance;
    }
  }
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

431

Zeph (./427) :
pourquoi le constructeur statique vide ?
C'est un peu tordu à expliquer, mais en gros ça change la façon dont le constructeur statique est appelé. (Un attribut CIL sur la classe .NET générée)
Comme tu t'en doutes, l'initialisation des membres statiques se fait en réalité dans le constructeur statique, de même que l'initialisation des membres d'instance se fait dans le constructeur d'instance.
La version par défaut de l'initialisation statique en C# (pas de constructeur statique) laisse une grande latitude au JIT pour l'initialisation des membres statiques. En gros, le constructeur peut-être appelé n'importe quand, que ce soit au début de ton programme, au chargement de la classe, avant l'appel d'une méthode de la classe, à l'intérieur d'une méthode de la classe, ou même jamais. Malgré tout, tu conserves la garantie que le champ sera toujours initialisé au moment où tu en auras besoin (tu ne sais juste pas quand il sera initialisé).
La version avec constructeur statique est plus logique d'un point de vue humain, et se contente d'appeler le constructeur statique la première fois que tu utilises un membre de la classe. (Techniquement, il y a des chances que le « if (classInitialized) » se retrouve au milieu d'une ou plusieurs méthodes de ton code, selon l'état de ton programme. Même si ça n'est qu'un détail d'implémentation, ça peut avoir son importance, si ce test ce retrouve calé au milieu d'une boucle serrée…)

Personnellement, je suis plutôt partisan de la version sans constructeur statique, mais ça veut dire que le singleton (ainsi que tous les autres membres statiques s'il y en a) va potentiellement être instancié très tôt (ou très tard, dans le meilleur des cas). Ce n'est pas vraiment désirable dans le cas d'un objet lourd en mémoire ou en temps d'initialisation.

(Je sais pas si c'est clair… Et on peut ptet déplacer ça dans un autre topic aussi tongue)

Pour documentation, voici la référence.

Folco (./428) :
GoldenCrystal (./426) :
Ça devrait économiser quelques octets de mémoire, et peut-être quelques cycles à chaque accès. (J'en suis presque certain, j'ai juste la flemme de faire les tests tongue.gif )
Bon écoute Golden, si vraiment tu veux progresser en programmation, arrête de compter les cycles et les octets pour les langages de haut niveau, tu m'as compris ?
grin


Kevin > Ne vaut-il pas mieux déplacer le code d'initialisation du singleton (le else dans ton code) dans une méthode séparée afin que la méthode getInstance() soit inlinée proprement ailleurs dans le code ?
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

432

-- Raté -- -_-'
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

433

GoldenCrystal (./431) :
Kevin > Ne vaut-il pas mieux déplacer le code d'initialisation du singleton (le else dans ton code) dans une méthode séparée afin que la méthode getInstance() soit inlinée proprement ailleurs dans le code ?

Si. smile (Enfin, pas pour la taille, mais pour la vitesse, oui.)
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

434

GoldenCrystal (./431) :
Comme tu t'en doutes, l'initialisation des membres statiques se fait en réalité dans le constructeur statique, de même que l'initialisation des membres d'instance se fait dans le constructeur d'instance.

Heu non, avant ? Dans les deux cas (statique ou instance), les membres sont initialisés puis le constructeur (éventuellement vide) est appelé : http://www.csharp411.com/c-object-initialization/

Ça se constate assez facilement en regardant le MSIL généré, ou en mettant des breakpoints dans un constructeur et en constatant que tous les champs sont initialisés avant d'entrer à l'intérieur. Je ne pense pas que définir ou non un constructeur vide (statique ou instance) change quoi que ce soit pour le coup.

Sinon dans ton article je ne vois pas une variante pourtant assez simple si on veut faire le double lock "à la main" :
if (instance == null) { lock (sync) { if (instance == null) Interlocked.Exchange (ref instance, new Singleton ()); } }
(la même avec le MemoryBarrier kivabien à la place de l'interlock exchange fonctionne également)
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

435

GoldenCrystal (./426) :
Pourquoi pas juste ça ? cheeky
        private static readonly ClientLibrary instance = new ClientLibrary();

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

        static ClientLibrary() { }

        private ClientLibrary() { }
Ça devrait économiser quelques octets de mémoire, et peut-être quelques cycles à chaque accès. (J'en suis presque certain, j'ai juste la flemme de faire les tests tongue)

Zeph > J'aurai quand même été fort surpris que ce soit un bug du runtime .NET que personne n'ait jamais détecté… Mais j'avoue que tu m'as fait douter à un moment grin

Effectivement, j'avais vu ça mais les explications n'étaient pas claires et je ne comprenais pas le pourquoi du bloc statique, du coup je n'ai pas utilisé. Ton article est beaucoup mieux happy
Sinon oué honte à moi, j'ai pourtant vérifié et revérifié avant de poster, mais elle était vraiment vicieuse cette tâche hehe
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

436

Zeph (./434) :
Sinon dans ton article je ne vois pas une variante pourtant assez simple si on veut faire le double lock "à la main" :

C'est la même que la mienne (./430). tongue
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

437

Oui mais la tienne c'est pas du C# donc on s'en tape tongue (si au moins t'avais eu le bon goût d'utiliser du C++11 au lieu d'ajouter une dépendance à Qt embarrassed)
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

438

Zeph (./434) :
GoldenCrystal (./431) :
Comme tu t'en doutes, l'initialisation des membres statiques se fait en réalité dans le constructeur statique, de même que l'initialisation des membres d'instance se fait dans le constructeur d'instance.

Heu non, avant ? Dans les deux cas (statique ou instance), les membres sont initialisés puis le constructeur (éventuellement vide) est appelé : http://www.csharp411.com/c-object-initialization/
Ça se constate assez facilement en regardant le MSIL généré, ou en mettant des breakpoints dans un constructeur et en constatant que tous les champs sont initialisés avant d'entrer à l'intérieur.
Heu…
J'ai vraiment du mal à te suivre là… Par quelle magie crois-tu que les champs (particulièrement les readonly grin) sont initialisées s'ils ne sont pas placés dans le constructeur ? cheeky
Ce que fait le compilateur c'est déplacer tout le code d'initialisation hors constructeur "C#" pour le mettre tout au début du(des) constructeur(s) de la classe .NET. (Statique ou Instance)
Ça peut compenser en partie le fait que contrairement à Java, .NET ne possède pas de blocs d'initialisation, mais bon…

Personnellement, j'évite d'initialiser les champs d'instance en dehors du constructeur (dans le code de la classe) parce que ça ne permet pas de factoriser le code d'initialisation des champs en présence de différents constructeurs. Par contre dans le cas des constructeurs statiques, il n'y a qu'un seul constructeur (« initialiseur de type »).
Je ne pense pas que définir ou non un constructeur vide (statique ou instance) change quoi que ce soit pour le coup.
Sauf comparaison aux constructeurs d'instance, je parlais spécifiquement des constructeurs statiques, et le pourquoi du comment est expliqué dans mon post ainsi que dans l'article : « ça change la façon dont le constructeur statique est appelé. (Un attribut CIL sur la classe .NET générée) ».

1 - C'est pas parce que tu déclares pas un constructeur en C# qu'il n'y a pas de constructeur. / L'initialisation des champs se fait *dans* le vrai constructeur.
2 - Si tu déclares explicitement un constructeur statique, ça change la façon dont le vrai constructeur statique sera apellé/utilisé.
Sinon dans ton article je ne vois pas une variante pourtant assez simple si on veut faire le double lock "à la main" :
if (instance == null) { lock (sync) { if (instance == null) Interlocked.Exchange (ref instance, new Singleton ()); } }
(la même avec le MemoryBarrier kivabien à la place de l'interlock exchange fonctionne également)
Heu… Mais quel est l'intérêt de Interlocked.Exchange ici ? confus
Le lock fait toutes les barrières mémoire nécessaires au double-checked locking, c'est d'ailleurs l'exemple numéro 3 dans le lien que j'ai posté ! tongue
(C'est aussi l'implémentation standard, celle qui ne fonctionnait pas en Java avant qu'ils changent le modèle mémoire, et celle qui n'est pas garantie strictement par le modèle mémoire ECMA, mais qui est à priori garanti par le modèle mémoire CLR de Microsoft)


Quelques références sur le modèle mémoire .NET :

http://blogs.msdn.com/b/cbrumme/archive/2003/05/17/51445.aspx (Qui explique entre autres que le modèle mémoire CLR 2.0 est plus fort que le modèle mémoire ECMA CLI… Et pourquoi c'est mieux.)
http://www.bluebytesoftware.com/blog/2007/11/10/CLR20MemoryModel.aspx
http://www.bluebytesoftware.com/blog/2008/06/13/VolatileReadsAndWritesAndTimeliness.aspx



Mais bon, tout ça pour dire que "public static readonly Singleton Default = new Singleton();" ftw tongue
(Qu'y a-t-il de plus efficace que la lecture d'un champ statique d'une classe… À part la lecture d'un registre ou d'une valeur sur le stack cheeky)
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

439

GoldenCrystal (./438) :
Heu…
J'ai vraiment du mal à te suivre là… Par quelle magie crois-tu que les champs (particulièrement les readonly grin) sont initialisées s'ils ne sont pas placés dans le constructeur ? cheeky

Peut-être qu'on parle de deux choses différentes, moi de ce qu'on appelle "constructeur" dans n'importe quel langage objet, donc ici le constructeur écrit en C# (et ce n'est *pas* lui qui s'occupe des initialisations des attributs), et le code de construction généré qui forcément doit s'occuper de tout le boulot, dans l'ordre (i.e. attributs puis constructeur).
1 - C'est pas parce que tu déclares pas un constructeur en C# qu'il n'y a pas de constructeur. / L'initialisation des champs se fait *dans* le vrai constructeur.

2 - Si tu déclares explicitement un constructeur statique, ça change la façon dont le vrai constructeur statique sera apellé/utilisé.

OK, cf. ci-dessus.
Heu… Mais quel est l'intérêt de Interlocked.Exchange ici ? confus
Le lock fait toutes les barrières mémoire nécessaires au double-checked locking, c'est d'ailleurs l'exemple numéro 3 dans le lien que j'ai posté ! tongue

Il y a vraiment la garantie que même sans memory barrier ça fonctionne, quel que soit le processeur et la version de .NET ? Il me semblait avoir entendu que non, mais je n'ai pas de lien sous la main.
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

440

GoldenCrystal (./438) :
Heu… Mais quel est l'intérêt de Interlocked.Exchange ici ? confus
Le lock fait toutes les barrières mémoire nécessaires au double-checked locking, c'est d'ailleurs l'exemple numéro 3 dans le lien que j'ai posté ! tongue

L'intérêt, c'est d'éviter d'avoir instance rempli à moitié quand on utilise le chemin rapide sans le lock (ligne 1). Une écriture atomique garantit de ne pas avoir de mauvaises surprises (sans devoir se farcir le lock à chaque fois dans le cas courant). J'ai fait exactement la même chose dans le ./430 (cf. mon utilisation de QAtomicPointer) sans avoir lu le code du ./434, l'auteur de la version C# a dû faire la même réflexion que moi.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

441

Après, si la CLR apporte la garantie que l'assignation ne sera effectuée qu'une fois l'initialisation entièrement réalisée, l'exchange n'est pas utile. Mais en plus de contredire ce que j'avais entendu jusqu'ici, qu'est-ce qui justifierait du coup l'existence de cette fonction ?
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

442

Zeph (./439) :
GoldenCrystal (./438) :
Heu…
J'ai vraiment du mal à te suivre là… Par quelle magie crois-tu que les champs (particulièrement les readonly grin) sont initialisées s'ils ne sont pas placés dans le constructeur ? cheeky
Peut-être qu'on parle de deux choses différentes, moi de ce qu'on appelle "constructeur" dans n'importe quel langage objet, donc ici le constructeur écrit en C# (et ce n'est *pas* lui qui s'occupe des initialisations des attributs), et le code de construction généré qui forcément doit s'occuper de tout le boulot, dans l'ordre (i.e. attributs puis constructeur).
Effectivement, pour moi celui qui compte surtout, c'est le constructeur .NET… tongue
Heu… Mais quel est l'intérêt de Interlocked.Exchange ici ? confus
Le lock fait toutes les barrières mémoire nécessaires au double-checked locking, c'est d'ailleurs l'exemple numéro 3 dans le lien que j'ai posté ! tongue

Il y a vraiment la garantie que même sans memory barrier ça fonctionne, quel que soit le processeur et la version de .NET ? Il me semblait avoir entendu que non, mais je n'ai pas de lien sous la main.
Ben, cf. ce les deux premiers liens que j'ai posté, normalement oui.
J'ajoute celui-là qui n'est plus disponible aujourd'hui sad (je savais qu'il me manquait un truc !) http://web.archive.org/web/20110605122922/http://msdn.microsoft.com/en-us/magazine/cc163715.aspx (Cet article est normalement relié aux autres mais bon… J'avais espéré le retrouver à une autre adresse et puis je l'ai oublié. Lui, est plus explicite)

C'est le plus proche d'une réponse 100% exacte que j'aie trouvé lorsque j'avais recherché ça à l'époque, et ça expliquait plus ou moins que le CLR 2.0 de Microsoft garantit des choses assez similaire au modèle x86. (Disons que dans le cas du double checked locking, tu pouvais prendre ça comme acquis que ça marcherait tout le temps)

Apparemment, les articles récents tendent à être plus contrastés: http://msdn.microsoft.com/en-us/magazine/jj553518.aspx
Cet article n'est pas très explicite, mais à priori il n'y aurait pas autant de garanties qu'en x86. On peut supposer qu'une barrière mémoire est placée à la fin des appels de constructeurs pour conserver un comportement similaire à ce qui est observé sur x86, mais ce n'est pas dit clairement. Par contre il explique qu'il faut à présent penser à marquer les variables comme volatile là où on ne le faisait pas auparavant. (En .NET, volatile implique des demi-barrières sur la lecture et sur l'écriture… Ce mot-clé ne sert pas énormément en .NET/x86)
La partie qui nous intéresse :
We’ve done targeted work to strengthen the memory model on ARM—specifically, we’ve inserted memory barriers at key points when writing to the managed heap to guarantee type safety—but we’ve made sure to only do this with a minimal impact on performance. The team went through multiple design reviews with experts to make sure that the techniques applied in the ARM CLR were correct. Moreover, performance benchmarks show that .NET code execution performance scales the same as native C++ code when compared across x86, x64 and ARM.
If your code relies on lock-free algorithms that depend on the implementation of the x86 CLR (rather than the ECMA CLR specification), you’ll want to add the volatile keyword to relevant variables as appropriate. Once you’ve marked shared state as volatile, the CLR will take care of everything for you. If you’re like most developers, you’re ready to run on ARM because you’ve already used locks to protect your shared data, properly marked volatile variables and tested your app on ARM.

C'est maigre, mais en attendant mieux…

Dans le cas du double-checked lock, marquer la variable comme volatile devrait être plus que suffisant, si jamais ça devait être vraiment nécessaire. (Puisque dans ce cas, tu es sûr que l'assignation du champ sera fait après la dernière assignation dans le constructeur, donc ton objet sera initialisé)
Sinon, j'aurais plutôt utilisé Thread.VolatileWrite() pour ça ^^
J'ose supposer que c'est plus efficace parce que ça ne verrouille pas forcément la mémoire, contraîrement au Interlocked, mais bon, c'est une opération faite une seule fois ça ne fera pas beaucoup de différence. (De mémoire les Thread.VolatileXXX sont des barrières mémoire complètes)

Bref, le mieux pour l'instant c'est surtout de pas le faire soi-même ! cheeky C'est simple, plus rapide, et plus efficace, que demande le peuple :]
(Et le double-checked locking est fourni par Lazy<T> s'il faut vraiment y avoir recours ^^)
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

443

Je te signale que justement, en x86 natif, il faut utiliser des instructions atomiques parce que sinon le double-checked lock n'est pas fiable, le pointeur pourrait avoir été écrit à moitié (et boum!). Donc s'il y a juste "autant de garanties qu'en x86", il faut bien le Interlocked.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

444

445

En C#, tu as une garantie que les opérations jusqu'à la taille d'un entier natif sont toujours atomique tant que tu respectes l'alignement.
Ça tombe bien, parce que c'est aussi le cas du x86 [et probablement de beaucoup de CPU modernes].
Et ça tombe encore mieux parce que c'est (à priori) justement toujours le cas des pointeurs [d'être alignés correctement]. (Et sauf erreur de ma part, c'est impossible qu'un type primitif [de taille ≤ taille d'un entier natif] aligné correctement dépasse d'une ligne de cache)
D'autre part, les primitives atomiques de synchronisation ne fonctionneront pas sur des champs non alignés avec tous les CPU.

Fort heureusement, cette fois-ci, tu racontes n'importe quoi tongue

8.1.1 Guaranteed Atomic Operations
The Intel486 processor (and newer processors since) guarantees that the following basic memory operations will always be carried out atomically:
• Reading or writing a byte
• Reading or writing a word aligned on a 16-bit boundary
• Reading or writing a doubleword aligned on a 32-bit boundary
The Pentium processor (and newer processors since) guarantees that the following additional memory operations will always be carried out atomically:
• Reading or writing a quadword aligned on a 64-bit boundary
• 16-bit accesses to uncached memory locations that fit within a 32-bit data bus
The P6 family processors (and newer processors since) guarantee that the following additional memory operation will always be carried out atomically:
• Unaligned 16-, 32-, and 64-bit accesses to cached memory that fit within a cache line
Accesses to cacheable memory that are split across cache lines and page boundaries are not guaranteed to be atomic by the Intel Core 2 Duo, Intel® AtomTM, Intel Core Duo, Pentium M, Pentium 4, Intel Xeon, P6 family, Pentium, and Intel486 processors. The Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium M, Pentium 4, Intel Xeon, and P6 family processors provide bus control signals that permit external memory subsystems to make split accesses atomic; however, nonaligned data accesses will seriously impact the performance of the processor and should be avoided. An x87 instruction or an SSE instructions that accesses data larger than a quadword may be implemented using multiple memory accesses. If such an instruction stores to memory, some of the accesses may complete (writing to memory) while another causes the operation to fault for architectural reasons (e.g. due an page-table entry that is marked “not present”). In this case, the effects of the completed accesses may be visible to software even though the overall instruction caused a fault. If TLB invalidation has been delayed (see Section 4.10.4.4), such page faults may occur even if all accesses are to the same page.
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

446

Bien sûr que ça marche sous x86, il n'a jamais été question de ça, le but était de savoir si c'était une garantie au niveau de la VM ; sinon, l'interlocked (./434) n'est pas une option. Quant aux effets de bord de "volatile" (donc ça n'est pas le rôle) là-dessus, c'est un sujet de troll à part entière.
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

447

Ce post était pour répondre à Kevin hein, pour toi la réponse est au dessus, mais c'est vrai que c'est un peu long a lire (désolé).
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

448

Non non j'ai bien noté, pas encore lu mais c'est dans la liste des choses à faire grin
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

449

avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

450

Bah oui, c'est une licence pour une seule machine, pas étonnant que leur Digital Restriction Management (DRM) le fasse valoir. Attendez-vous à une vérification de plus en plus fasciste des licences, bientôt WGA gardera une connexion Internet active pour bien vérifier qu'une seule machine utilise la licence, comme certains jeux le font déjà. Mais ces changements se font par petites étapes, histoire d'éviter une révolution des utilisateurs. Bienvenue dans le monde du logiciel propriétaire!
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité