Question

I lu dans la documentation de MS que l'attribution d'une valeur de 64 bits sur un ordinateur Intel 32 bits ne constitue pas une opération atomique; à savoir, l'opération est pas sûre. Cela signifie que si deux personnes attribuent simultanément une valeur à un champ de Int64 statique, la valeur finale du champ ne peut pas être prédit.

Troisième partie question:

  • Est-ce vraiment vrai?
  • Est-ce que je me inquiéterais dans le monde réel?
  • Si ma demande est multi-thread-je vraiment besoin d'entourer tous mes devoirs de Int64 avec le code de verrouillage?
Était-ce utile?

La solution

On ne parle pas toutes les variables que vous rencontrez. Si une variable est utilisée comme un état partagé ou quelque chose (y compris, mais sans s'y limiter à certains champs de static), vous devez prendre soin de cette question. Il est entièrement non-émission pour les variables locales qui ne sont pas hissés par conséquent être fermé sur une fermeture ou une transformation itérateur et qui sont utilisés par une fonction unique (et donc, un seul fil) à la fois.

Autres conseils

Même si les écritures étaient atomiques, les chances sont que vous devez toujours prendre un verrou chaque fois que vous avez accédé à la variable. Si vous ne l'avez pas, vous auriez au moins avoir à faire la volatile variable pour faire en sorte que toutes les discussions ont vu la nouvelle valeur la prochaine fois qu'ils ont lu la variable (ce qui est presque toujours ce que vous voulez). Cela vous permet de faire atomiques, ensembles volatils -. Mais dès que vous voulez faire quelque chose de plus intéressant, comme l'ajout de 5 à, vous reviendriez à verrouillage

programmation libre Lock est très, très difficile à obtenir droite. Vous devez savoir exactement ce que vous faites, et de garder la complexité comme petit un morceau de code possible. Personnellement, j'essaie rarement même de tenter autre que des modèles très bien connus, tels que l'aide d'un initialiseur statique pour initialiser une collection, puis la lecture de la collection sans verrouillage.

Utilisation de la classe Interlocked peut aider dans certains situations, mais il est presque toujours beaucoup plus facile de prendre juste un verrou. serrures incontestées sont « assez pas cher » (il est vrai qu'ils obtiennent cher avec plus de cœurs, mais le fait tout.) - ne plaisante pas avec le code sans verrou jusqu'à ce que vous avez une bonne preuve que ça va réellement faire une différence significative

MSDN :

  

Attribution d'une instance de ce type est   pas thread-safe sur tout le matériel   plates-formes parce que le binaire   représentation de cette instance pourrait   être trop grand pour assigner en une seule   opération atomique.

Mais aussi:

  

Comme tout autre type, la lecture et   écriture à une variable partagée   contient une instance de ce type doit   être protégée par une serrure pour garantir   fil de sécurité.

Si vous avez une variable partagée (par exemple, comme un champ statique d'une classe, ou comme champ d'un objet partagé), et ce champ ou d'un objet va être utilisé contre-fil, alors, oui, vous avez besoin pour vous assurer que l'accès à cette variable est protégée par une opération atomique. Le processeur x86 a intrinsics pour vous assurer que cela se produit, et cette installation est exposée à travers les méthodes de classe System.Threading.Interlocked.

Par exemple:

class Program
{
    public static Int64 UnsafeSharedData;
    public static Int64 SafeSharedData;

    static void Main(string[] args)
    {
        Action<Int32> unsafeAdd = i => { UnsafeSharedData += i; };
        Action<Int32> unsafeSubtract = i => { UnsafeSharedData -= i; };
        Action<Int32> safeAdd = i => Interlocked.Add(ref SafeSharedData, i);
        Action<Int32> safeSubtract = i => Interlocked.Add(ref SafeSharedData, -i);

        WaitHandle[] waitHandles = new[] { new ManualResetEvent(false), 
                                           new ManualResetEvent(false),
                                           new ManualResetEvent(false),
                                           new ManualResetEvent(false)};

        Action<Action<Int32>, Object> compute = (a, e) =>
                                            {
                                                for (Int32 i = 1; i <= 1000000; i++)
                                                {
                                                    a(i);
                                                    Thread.Sleep(0);
                                                }

                                                ((ManualResetEvent) e).Set();
                                            };

        ThreadPool.QueueUserWorkItem(o => compute(unsafeAdd, o), waitHandles[0]);
        ThreadPool.QueueUserWorkItem(o => compute(unsafeSubtract, o), waitHandles[1]);
        ThreadPool.QueueUserWorkItem(o => compute(safeAdd, o), waitHandles[2]);
        ThreadPool.QueueUserWorkItem(o => compute(safeSubtract, o), waitHandles[3]);

        WaitHandle.WaitAll(waitHandles);
        Debug.WriteLine("Unsafe: " + UnsafeSharedData);
        Debug.WriteLine("Safe: " + SafeSharedData);
    }
}

Les résultats:

  

Unsafe : -24.050.275.641    Sécurité : 0

Sur une note intéressante, j'ai couru ce en mode 64 bits sur Vista 64. Cela montre que 64 champs de bits sont traités comme des 32 champs de bits par le moteur d'exécution, qui est, 64 opérations de bits sont non-atomique. Quelqu'un sait si cela est une question CLR ou un problème x64?

Sur une plate-forme x86 32 bits la plus grande partie de la mémoire de taille atomique est de 32 bits.

Cela signifie que si quelque chose écrit ou lit d'une variable de taille 64 bits, il est possible que lecture / écriture pour obtenir préempté lors de l'exécution.

  • Par exemple, vous commencez à attribuer une valeur à une variable 64 bits.
  • Après les 32 premiers bits sont écrits l'OS décide qu'un autre processus va obtenir du temps CPU.
  • Le processus suivant tente de lire la variable que vous étiez au milieu d'attribuer à.

C'est juste une condition de course possible avec affectation 64 bits sur une plate-forme 32 bits.

Cependant, même avec 32 variable bit il peut y avoir des conditions de course avec la lecture et l'écriture à cet effet une variable partagée doivent être synchronisés d'une certaine façon de résoudre ces conditions de course.

Est-ce vraiment vrai? Oui, il se trouve. Si vos registres ont seulement 32 bits en eux, et que vous devez stocker une valeur 64 bits vers un emplacement de mémoire, il va prendre deux opérations de chargement et deux opérations en magasin. Si votre processus est interrompu par un autre processus entre ces deux charges / magasins, l'autre moitié processus pourrait corrompre vos données! Etrange mais vrai. Cela a été un problème sur chaque processeur jamais construit -. Si votre type de données est plus long que vos registres, vous avez des problèmes de concurrence

Est-ce que je me inquiéterais dans le monde réel? Oui et non. Depuis presque toute la programmation moderne reçoit son propre espace d'adressage, vous aurez seulement besoin de vous en soucier si vous faites la programmation multi-thread.

Si ma demande est ne multithread-je vraiment besoin d'entourer toutes mes missions Int64 avec le code de verrouillage? Malheureusement, oui si vous voulez obtenir technique. Il est généralement plus facile dans la pratique d'utiliser un Mutex ou Sémaphore autour de blocs de code plus grand que pour verrouiller chaque instruction individuelle sur les variables ensemble globalement accessibles.

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