Pourquoi les objects immuables sont-ils thread-safe?

class Unit { private readonly ssortingng name; private readonly double scale; public Unit(ssortingng name, double scale) { this.name = name; this.scale = scale, } public ssortingng Name { get { return name; } } public ssortingng Scale { get { return scale; } } private static Unit gram = new Unit("Gram", 1.0); public Unit Gram { get { return gram; } } } 

Plusieurs threads ont access à Unit.Gram . Pourquoi est-il Unit.Gram.Title plusieurs threads lisent simultanément Unit.Gram.Title ?

Ce qui m’inquiète, c’est qu’ils se réfèrent au même emplacement mémoire. Un thread commence à lire cette mémoire, alors n’est-il pas “verrouillé”? Est-ce que le .NET gère la synchronisation pour cette section critique en dessous? Ou est-ce que je me trompe en pensant que la lecture simultanée doit être synchronisée?

Je pense que votre question ne concerne pas la sécurité des threads ou l’immuabilité, mais concerne les détails (très) bas niveau de l’access à la mémoire.

Et c’est un sujet lourd, mais la réponse courte est: Oui, deux threads (et plus important encore, plus de 2 CPU) peuvent lire (et / ou écrire) le même morceau de mémoire simultanément.

Et tant que le contenu de cette zone de mémoire est immuable, tous les problèmes sont résolus. Quand cela peut changer, il y a toute une gamme de problèmes, le mot-clé volatile et la classe Interlocked sont quelques-uns des outils que nous utilisons pour les résoudre.

Qu’est-ce qui fait qu’un object n’est pas thread-safe? Un object n’est pas thread-safe si la valeur / l’état de cet object peut changer pendant qu’un thread le lit. Cela se produit généralement si un deuxième thread modifie la valeur de cet object pendant que le premier thread le lit.

Un object immuable, par définition, ne peut pas changer de valeur / d’état. Comme chaque fois que vous lisez un object immuable, celui-ci a la même valeur / le même état, vous pouvez avoir un nombre quelconque de threads lisant cet object sans souci.

Les lectures simultanées n’ont pas besoin de synchronisation. Etant donné que la synchronisation n’est requirejse que pour les écrivains (ou les lecteurs et au moins un écrivain), les objects immuables ne nécessitent pas de synchronisation et sont donc threadsafe.

Si l’object est immuable, son état ne changera jamais. Par conséquent, les préoccupations concernant les données obsolètes disparaissent. La lecture de fil n’est jamais verrouillée, il ne s’agit donc pas d’un problème (impasse)

Les lectures simultanées ne nécessitent pas de synchronisation dans la grande majorité des cas (les exceptions sont des choses telles que les E / S mappées en mémoire où la lecture d’une adresse donnée peut provoquer des effets indésirables).

Si les lectures simultanées nécessitaient une synchronisation, il serait presque impossible d’écrire du code utilement multiniveau. Pour exécuter un morceau de code, par exemple, le processeur doit lire le stream d’instructions. Comment écririez-vous une fonction de locking si la fonction elle-même devait se protéger de son exécution simultanée :)?

La lecture du même emplacement mémoire ne peut être effectuée en un cycle de la CPU que par un thread spécifique. L’ordre de lecture dans ce cas importe peu puisque la valeur sous-jacente ne change pas. Par conséquent, il n’y a aucune possibilité qu’une lecture soit incohérente, il n’y a donc pas besoin de synchronisation à aucun niveau dans ce cas.

Les unités de gestion de la mémoire sont la partie du processeur qui gère la lecture de la mémoire. Si vous en avez plusieurs, une fois dans une lune bleue, deux d’entre eux pourraient essayer de lire le même emplacement mémoire au cours de la même douzaine de nanosecondes, mais aucun problème ne se produit alors qu’ils obtiennent la même réponse.

Outre des exceptions telles que la mémoire mappée pour les pilotes, par exemple, il n’y a aucun problème pour deux threads lisant simultanément la même adresse mémoire. Un problème peut survenir lorsqu’un thread effectue l’ écriture de données. Dans ce cas, d’autres threads peuvent être empêchés de lire cet object / cette donnée.

Mais le problème n’est pas dû à la simultanéité des écrits (de toute façon, au niveau électronique le plus bas, ils se produisent l’un après l’autre), le problème est plutôt que l’object / l’dataset peut perdre sa cohérence. Généralement, on utilise un section critic pour isoler du code qui ne peut pas être lu / écrit simultanément par d’autres threads.

Il existe de nombreux exemples sur le Net, mais considérons ce qui suit, avec price est un membre privé d’une classe, disons Product, qui possède également 2 méthodes.

 public void setPrice(int value) { price = value; // -- point CRITIC -- price += TAX; } public int getPrice() { return price; } 

setPrice (v) définit le prix d’un produit sur v et l’ajuste à la TVA (le programme doit avoir la value += TAX; price = value mais ce n’est pas le point ici 🙂

Si le fil A écrit le prix 100 et que TAX est (fixe) 1, le prix du produit sera finalement fixé à 101. Mais que se passera-t-il si le fil B lit le prix via getPrice () alors que le fil A est au point CRITIC ? Le prix retourné à B manquera la taxe, et sera faux.

setPrice () devrait dans une section critique ( lock ), pour empêcher tout access à l’object pendant la fixation du prix

  lock(this) { price = value; price += TAX; }