Pregunta

He leído en la documentación de MS que la asignación de un valor de 64 bits en un equipo Intel de 32 bits no es una operación atómica; es decir, la operación no se hilo de seguridad. Esto significa que si dos personas se asignan al mismo tiempo un valor a un campo Int64 estática, el valor final del campo, no pueden predecirse.

Tres cuestión parte:

  • ¿Es esto realmente cierto?
  • ¿Es esto algo que iba a preocuparse en el mundo real?
  • Si mi solicitud es multi-hilo lo que realmente necesita para rodear todas mis tareas Int64 con el código de bloqueo?
¿Fue útil?

Solución

No se trata de todas las variables que encuentre. Si se utiliza alguna variable como un estado compartido o algo (incluyendo, pero no limitado a algunos campos static), se debe tener cuidado de este problema. Es completamente no-problema para las variables locales que no se izan como consecuencia de ser cerrado otra vez en un cierre o una transformación iterador y son utilizados por una única función (y por lo tanto, un solo hilo) a la vez.

Otros consejos

Aunque las escrituras fueron atómicas, lo más probable es que usted todavía tiene que obtener un bloqueo cada vez que accede a la variable. Si no hacer eso, usted por lo menos tiene que hacer el volatile variable para asegurarse de que todas las discusiones vieron al nuevo valor que la próxima vez que leen la variable (que es casi siempre lo que quiere). Que le permite hacer conjuntos atómicas, volátiles -., Pero tan pronto como se quiere hacer algo más interesante, como la adición de 5 a ella, que estaría de vuelta a bloqueo

Bloqueo de programación libre es muy, muy duro para hacerlo bien. Lo que necesita saber exactamente lo que está haciendo, y mantener la complejidad a una pieza tan pequeña de código como sea posible. En lo personal, rara vez incluso intento para intentar que no sea para patrones muy conocidos, tales como el uso de un inicializador estático para inicializar una colección y luego la lectura de la colección sin bloquear.

Uso de la clase Interlocked puede ayudar de alguna situaciones, pero es casi siempre mucho más fácil de tomar sólo un bloqueo. cerraduras no impugnados son "bastante barato" (la verdad es que ser caro con más núcleos, pero también lo hace todo). - No perder el tiempo con código libre de bloqueo hasta que haya logrado una buena evidencia de que en realidad va a hacer una diferencia significativa

MSDN :

  

Asignación de una instancia de este tipo se   no seguro para subprocesos en todo el hardware   Debido a que las plataformas binaria   representación de esa instancia podría   ser demasiado grande para asignar en una sola   operación atómica.

Pero también:

  

Al igual que con cualquier otro tipo, la lectura y   escrito a una variable compartida que   contiene una instancia de este tipo debe   estar protegido por una cerradura para garantizar   seguridad hilo.

Si usted tiene una variable compartida (por ejemplo, como un campo estático de una clase, o como campo de un objeto compartido), y ese campo u objeto va a ser utilizado a través del hilo, entonces, sí, es necesario para asegurarse de que el acceso a esa variable está protegido por medio de una operación atómica. El procesador x86 tiene intrínsecos para asegurarse de que esto ocurra, y esta instalación está expuesta a través de los métodos de la clase System.Threading.Interlocked.

Por ejemplo:

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

Los resultados:

  

inseguro : -24,050,275,641    seguro 0

En una nota interesante, me encontré con esto en el modo de 64 bits de Vista 64. Esto muestra que 64 campos de bits son tratados como 32 campos de bits por el tiempo de ejecución, es decir, 64 bits son operaciones no atómica. Alguien sabe si esto es un problema de CLR o un problema x64?

En una plataforma x86 de 32 bits de la pieza más grande de tamaño atómico de la memoria es de 32-bits.

Esto significa que si algo escribe o lee a partir de una variable de tamaño de 64 bits que es posible para que la lectura / escritura para conseguir vaciado anteriormente durante la ejecución.

  • Por ejemplo, se empieza a asignar un valor a una variable de 64 bits.
  • Después de los primeros 32 bits se escriben el sistema operativo decide que otro proceso se va a poner el tiempo de CPU.
  • El siguiente proceso intenta leer la variable que estaban en el medio de asignar a.

Eso es sólo una condición de carrera es posible con la asignación de 64 bits en una plataforma de 32 bits.

Sin embargo, incluso con la variable de 32 bits no puede haber condiciones de carrera con la lectura y escritura para ello cualquier variable compartida deben estar sincronizados de alguna manera de resolver estas condiciones de carrera.

¿Es esto realmente cierto? Sí, como resulta. Si sus registros sólo tienen 32 bits en ellos, y que necesita para almacenar un valor de 64 bits en cierta posición de memoria, que va a tomar dos operaciones de carga y dos operaciones de la tienda. Si el proceso se interrumpe por otro proceso entre estos dos almacenes de carga /, la otra mitad de proceso puede corromper sus datos! Extraño pero cierto. Esto ha sido un problema en todos los procesadores que se haya construido. - si su tipo de datos es más largo que sus registros, usted tiene problemas de concurrencia

Es esto algo que iba a preocuparse en el mundo real? Si y no. Puesto que casi toda la programación moderna se le da su propio espacio de direcciones, sólo tendrá que preocuparse por esto, si está haciendo la programación multi-hilo.

Si mi solicitud es multi-hilo lo que realmente necesita para rodear todas mis tareas Int64 con el código de bloqueo? Lamentablemente, sí, si usted quiere conseguir técnico. Por lo general es más fácil en la práctica de utilizar un mutex o semáforo en torno a bloques de código más grandes que para bloquear todas las declaraciones conjunto individual de las variables globalmente accesibles.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top