2010-08-29 11 views
29
class Unit { 
    private readonly string name; 
    private readonly double scale; 

    public Unit(string name, double scale) { 
     this.name = name; 
     this.scale = scale, 
    } 

    public string Name { get { return name; } } 
    public string Scale { get { return scale; } } 

    private static Unit gram = new Unit("Gram", 1.0); 

    public Unit Gram { get { return gram; } } 
} 

Varios hilos tienen acceso a Unit.Gram. ¿Por qué está bien que varios hilos lean simultáneamente Unit.Gram.Title?¿Por qué los objetos inmutables son seguros para subprocesos?

Mi preocupación es que se están refiriendo a la misma ubicación de memoria. Un hilo comienza a leer esa memoria, entonces, ¿no está "bloqueado"? ¿El .NET maneja la sincronización para esta sección crítica debajo? ¿O me equivoco al pensar que la lectura simultánea necesita sincronización?

+0

"Mi preocupación es que se están refiriendo a la misma ubicación de memoria". ¿Son ellos? Tal vez. Esa no es tu preocupación, es la preocupación de VM. La lectura simultánea no necesita sincronización, ¿por qué? –

+2

* "¿por qué" * >> no es esa la pregunta aquí? Si leo el mismo libro que mi novia lee, al mismo tiempo, siempre terminamos peleando por eso. Sé que no es un problema en las computadoras (no se producen dos hilos al mismo tiempo, la arquitectura de la CPU se encarga de eso), pero me parece una pregunta muy legítima :) – Abel

+0

Leer un libro implica un estado mutable (página actual, rayo, etc.). No estás peleando por el libro, sino por el procesamiento del mismo. – sisve

Respuesta

11

creo que su pregunta resulta no estar a punto de hilo de seguridad o inmutablity pero sobre los detalles (muy) bajos del acceso a la memoria.

Y ese es un tema importante, pero la respuesta corta es: Sí, dos hilos (y más importante, 2 + CPU) pueden leer (y/o escribir) la misma pieza de memoria simultáneamente.

Y mientras el contenido de esa área de memoria sea inmutable, se resuelven todos los problemas. Cuando puede cambiar, existe toda una gama de problemas, la palabra clave volatile y la clase Interlocked son algunas de las herramientas que utilizamos para resolverlos.

+1

En un nivel bajo, como dices, creo que el acceso simultáneo a la memoria no ocurre. Los procesadores no pueden acceder a la misma memoria al mismo tiempo, a menos que esa memoria se almacene en caché. De lo contrario, se debe usar el bus de memoria, que solo puede ser utilizado por un procesador al mismo tiempo. Esto paraliza los procesadores y limita la ganancia de rendimiento que varios procesadores podrían ofrecer (el acceso a la memoria es varias veces más lento que la CPU). Un remedio es el [concepto o arquitectura NUMA] (http://en.wikipedia.org/wiki/Non-Uniform_Memory_Access), donde se usan buses separados para acceder (aún separar) las ubicaciones de memoria: – Abel

+0

@Abel, tienes razón pero lo llamé "la historia corta": en lo que nosotros (los programadores) podemos decir, es simultáneo. Incluso para programadores de lenguaje ensamblador. –

+0

Creo que estás en lo correcto. Pero la ramificación de la sincronización de lectura de memoria que se maneja de forma transparente es, exactamente, inmutabilidad ergo thread-safety. La respuesta real parece ser que debido a ** se maneja debajo **, y cuestionar si se manejó no fue tan estúpido después de todo. :) – randomguy

4

Si el objeto es inmutable, su estado nunca cambiará. Por lo tanto, las preocupaciones de los datos obsoletos salen por la ventana. La lectura de subprocesos nunca está bloqueada, por lo tanto, esto no es un problema (interbloqueo)

57

¿Qué hace que un objeto no sea seguro para subprocesos? Un objeto no es seguro para subprocesos si el valor/estado de ese objeto puede cambiar mientras un hilo lo está leyendo. Esto generalmente ocurre si un segundo hilo cambia el valor de este objeto mientras el primer hilo lo está leyendo.

Un objeto inmutable, por definición, no puede cambiar el valor/estado. Como cada vez que lee un objeto inmutable tiene el mismo valor/estado, puede tener cualquier cantidad de hilos que lean ese objeto sin preocupaciones.

+15

Puede agregar que cuando se inicializa un objeto inmutable, su estado * no * cambia, pero el CLR garantiza que este proceso sea seguro para subprocesos (es decir, no es posible que otro subproceso intente leer mientras que el proceso de inicio no sea terminado en el ctor o cctor). – Abel

+0

@Abel: Siento que su comentario es correcto, pero ¿podría traer una referencia al estándar de idioma que respalda su reclamo?En particular, ¿por qué es imposible que las escrituras en el constructor se muevan más allá de la publicación del objeto? – Vlad

11

Lecturas simultáneas no necesita sincronización. Como la sincronización solo es necesaria para los escritores (o los lectores y al menos un escritor), los objetos inmutables no requieren sincronización y, por lo tanto, son seguros para la realización de los hilos.

3

Las lecturas concurrentes no requieren sincronización en la gran mayoría de los casos (las excepciones son cosas como IO con memoria asignada donde la lectura desde una dirección determinada puede causar efectos secundarios).

Si las lecturas concurrentes requerían sincronización, sería prácticamente imposible escribir código multihilo de manera útil. Para ejecutar un fragmento de código, por ejemplo, el procesador debe leer la secuencia de instrucciones, ¿cómo escribiría una función de bloqueo si la función misma tuviera que protegerse contra la ejecución simultánea de este?

+2

Para ser justos, en algún nivel, el hardware tiene que lidiar con estos problemas. Pero está hecho en hardware y ni siquiera es visible para el sistema operativo, por no mencionar el escritor de aplicaciones .NET. – siride

+0

@siride, de hecho creo que debería aceptar su comentario como la respuesta porque realmente responde la pregunta. ** La sincronización se maneja debajo. ** Después de eso, es fácil entender la inmutabilidad en mi humilde opinión. – randomguy

+1

@randomguy, Bueno, _alguno_ hardware tiene que lidiar con estos problemas en algunos niveles, pero también presenta las necesidades en primer lugar en algunos niveles también. Si quiere decir que está "manejado debajo", lo que tiene que decir es "en algunos diseños de computadora donde las lecturas simultáneas podrían causar problemas debido a las elecciones de diseño, la posibilidad de que el software se vea afectado se elimina a través de varios mecanismos, otros diseños no se ve afectado por las lecturas concurrentes, y en otros, las lecturas concurrentes son imposibles ". –

1

Memory Management Units son la parte del procesador que maneja la lectura de la memoria. Si tiene más de uno, una vez en una luna azul, 2 de ellos podrían tratar de leer la misma ubicación de memoria en la misma docena de nano segundos, pero no aparecen problemas ya que obtienen la misma respuesta.

1

Además de excepciones como la memoria mapeada para los controladores, por ejemplo, no hay ningún problema para que dos hilos lean simultáneamente la misma dirección de memoria. Un problema puede surgir cuando un hilo realiza algunos escribiendo datos sin embargo. En este caso, otros hilos pueden evitarse para leer ese objeto/datos.

Pero el problema no es debido a la simultaneidad de los escritos (en el nivel más bajo electrónica que ocurren uno tras otro todos modos), el problema es más bien que el objeto/conjunto de datos puede perder su consistencia. Usualmente uno utiliza un section critic para aislar algún código que puede no ser leído/escrito simultáneamente por otros hilos.

Hay muchos ejemplos sobre la red, pero tenga en cuenta lo siguiente, con price es un miembro privado de una clase, digamos del producto, que también tiene 2 métodos

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

public int getPrice() { 
    return price; 
} 

setPrice (v) fija el precio de un producto a v, y ajústelo con IVA (el programa debería tener value += TAX; price = value pero este no es el punto aquí :-)

Si el hilo A escribe el precio 100, y el TAX es (fijo) 1, el precio del producto finalmente establecerse en 101. Pero, ¿qué sucede si el hilo B lee el precio a través de getPrice() mientras que el hilo A está en point CRITIC? El precio devuelto a B perderá el IMPUESTO y se equivocará.

setPrice() debe dentro de una sección crítica (lock), para evitar cualquier acceso al objeto durante la fijación del precio

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

La lectura de la misma ubicación de memoria solo se puede realizar en un ciclo de CPU por un hilo específico. El orden de lectura en este caso no importa ya que el valor subyacente no cambia. Por lo tanto, no hay posibilidad de que una lectura sea inconsistente, por lo que no es necesario sincronizar en ningún nivel en este caso.

+0

Creo que esto también responde muy bien a la pregunta. ¿Te importaría ampliar esta respuesta? – randomguy

+0

En realidad, 2 CPU podrían leer la misma dirección en la misma marca de reloj (y ver valores diferentes). Esto se debe a que cada uno leería desde su propio caché. –

+0

Eso es correcto, pero a los fines de la pregunta y la simplificación tuve que asumir una sola situación de CPU. Sin embargo, la única parte que cambia en una situación de CPU múltiple es la posibilidad de almacenamiento en memoria caché, pero el valor sigue siendo el mismo. Aunque el gramo variable se declara estático, no hay ningún "setter" para él y es inicializado al valor de 1.0 una vez. Por lo tanto, es inmutable y no necesita ninguna sincronización de programa. – venky

Cuestiones relacionadas