Pregunta

¿Qué quieres decir con instrucciones atómicas?

¿Cómo se vuelve atómico lo siguiente?

Prueba y conjunto

int TestAndSet(int *x){
   register int temp = *x;
   *x = 1;
   return temp;
}

Desde la perspectiva del software, si no se desean utilizar primitivas de sincronización sin bloqueo, ¿cómo se puede garantizar la atomicidad de la instrucción?¿Es posible solo en Hardware o se puede utilizar alguna optimización de directivas a nivel de ensamblaje?

¿Fue útil?

Solución

Algunas instrucciones de la máquina son intrínsecamente atómica -. Por ejemplo, la lectura y la escritura de valores correctamente alineados del procesador de textos es atómico nativa de tamaño en muchas arquitecturas

Esto significa que las interrupciones de hardware, procesadores y otros hiperprocesos no pueden interrumpir la lectura o almacenar y leer o escribir un valor parcial en la misma ubicación.

cosas más complicadas como la lectura y la escritura en conjunto atómicamente se puede lograr mediante instrucciones de máquina atómicos explícitos, por ejemplo, BLOQUEO cmpxchg en x86.

Locking y otra de alto nivel constructos se basan en estas primitivas atómica, que normalmente sólo se guardan un solo procesador de textos.

Algunos algoritmos inteligentes concurrentes pueden ser construidos usando sólo la lectura y la escritura de los punteros, por ejemplo, en las listas enlazadas compartidos entre un solo lector y escritor, o con esfuerzo, múltiples lectores y escritores.

Otros consejos

Atómica viene del griego (ἄτομος atomos) que significa "indivisible". (Advertencia: no hablo griego, así que quizás es realmente algo más, pero la mayoría de personas de habla inglesa citando etimologías interpretar de esta manera: -.)

En informática, esto significa que la operación, así, sucede . No hay ningún estado intermedio que es visible antes de que se complete. Así que si su CPU se interrumpe el servicio al hardware (IRQ), o si otra CPU está leyendo la misma memoria, que no afecta el resultado, y estas otras operaciones observará como sea completado o no se ha iniciado.

A modo de ejemplo ... digamos que desea establecer una variable a algo, pero sólo si no se ha especificado antes. Usted podría estar inclinado a hacer esto:

if (foo == 0)
{
   foo = some_function();
}

Pero lo que si esta se ejecuta en paralelo? Podría ser que el programa buscará foo, lo ven como cero, mientras tanto el hilo 2 viene y hace lo mismo y define el valor de algo. De vuelta en el hilo original, el código todavía piensa foo es cero, y la variable se le asigna dos veces.

En casos como este, la CPU proporciona algunas instrucciones que pueden hacer la comparación y la asignación condicional como una entidad atómica. Por lo tanto, la prueba-y-set, de comparación y de intercambio, y la carga ligada / tienda condicional. Puede utilizar estos para poner en práctica las cerraduras (su sistema operativo y su biblioteca de C ha hecho esto.) O puede escribir algoritmos de una sola vez que se basan en las primitivas que hacer algo. (Hay cosas interesantes que hacer aquí, pero lo más simples mortales evitar esto por temor a equivocarse.)

La atomicidad es un concepto clave cuando se tiene ningún tipo de procesamiento en paralelo (incluyendo diferentes aplicaciones que cooperan o comparten datos) que incluye recursos compartidos.

El problema está bien ilustrado con un ejemplo. Digamos que usted tiene dos programas que desea crear un archivo, pero sólo si ya no existe el archivo. Cualquiera de los dos programas puede crear el archivo en cualquier punto en el tiempo.

Si (Voy a usar C, ya que es lo que hay en el ejemplo):

 ...
 f = fopen ("SYNCFILE","r");
 if (f == NULL) {
   f = fopen ("SYNCFILE","w");
 }
 ...

no se puede estar seguro de que el otro programa no ha creado el archivo entre su abierto para lectura y su abierto para escritura.

No hay manera de que usted puede hacer esto por su cuenta, necesita la ayuda del sistema operativo, que generalmente proporcionan primitivas sincronización para este propósito, u otro mecanismo que se garantiza que sea atómica (por ejemplo, una base de datos relacional en la operación de bloqueo es atómica, o un mecanismo de nivel inferior como procesadores de "prueba y ajuste" instrucciones).

A continuación se muestran algunas de mis notas sobre Atomicidad que pueden ayudarle a comprender el significado.Las notas provienen de las fuentes enumeradas al final y recomiendo leer algunas de ellas si necesita una explicación más detallada en lugar de viñetas puntuales como las que tengo yo.Por favor señale cualquier error para que pueda corregirlo.

Definición :

  • Del griego que significa "no divisible en partes más pequeñas".
  • Siempre se observa que una operación "atómica" se realiza o no se realiza, pero nunca se realiza a la mitad.
  • Se debe realizar una operación atómica por completo o no.
  • En escenarios de subprocesos múltiples, una variable pasa de no mutada a mutado directamente, sin valores de "mutación a medias"

Ejemplo 1 :Operaciones atómicas

  • Considere los siguientes números enteros utilizados por diferentes subprocesos:

     int X = 2;
     int Y = 1;
     int Z = 0;
    
     Z = X;  //Thread 1
    
     X = Y;  //Thread 2
    
  • En el ejemplo anterior, dos subprocesos utilizan X, Y y Z.

  • Cada lectura y escritura son atómicas.
  • Los hilos correrán:
    • Si gana el hilo 1, entonces Z = 2
    • Si gana el hilo 2, entonces Z=1
    • Z definitivamente será uno de esos dos valores.

Ejemplo 2:Operaciones no atómicas:++/-- Operaciones

  • Considere las expresiones de incremento/decremento:

    i++;  //increment
    i--;  //decrement
    
  • Las operaciones se traducen en:

    1. leer yo
    2. Incrementar/disminuir el valor leído
    3. Escribe el nuevo valor nuevamente en i
  • Cada una de las operaciones se compone de 3 operaciones atómicas y no son atómicas en sí mismas.
  • Dos intentos de incrementar i en subprocesos separados podrían intercalarse de modo que se pierda uno de los incrementos

Ejemplo 3: operaciones no atómicas:Valores superiores a 4 bytes

  • Considere la siguiente estructura inmutable:
  struct MyLong
   {
       public readonly int low;
       public readonly int high;

       public MyLong(int low, int high)
       {
           this.low = low;
           this.high = high;
       }
   }
  • Creamos campos con valores específicos de tipo MyLong:

    MyLong X = new MyLong(0xAAAA, 0xAAAA);   
    MyLong Y = new MyLong(0xBBBB, 0xBBBB);     
    MyLong Z = new MyLong(0xCCCC, 0xCCCC);
    
  • Modificamos nuestros campos en hilos separados sin seguridad para hilos:

    X = Y; //Thread 1                                  
    Y = X; //Thread 2
    
  • En .NET, al copiar un tipo de valor, CLR no llama a un constructor: mueve los bytes una operación atómica a la vez.

  • Debido a esto, las operaciones en los dos subprocesos ahora son cuatro operaciones atómicas.
  • Si no se aplica la seguridad de subprocesos, los datos pueden dañarse
  • Considere el siguiente orden de ejecución de las operaciones:

    X.low = Y.low;      //Thread 1 - X = 0xAAAABBBB            
    Y.low = Z.low;      //Thread 2 - Y = 0xCCCCBBBB              
    Y.high = Z.high;    //Thread 2 - Y = 0xCCCCCCCC             
    X.high = Y.high;    //Thread 1 - X = 0xCCCCBBBB   <-- corrupt value for X
    
  • Leer y escribir valores superiores a 32 bits en múltiples subprocesos en un sistema operativo de 32 bits sin agregar algún tipo de bloqueo para que la operación sea atómica probablemente resulte en datos corruptos como se indicó anteriormente.

Operaciones del procesador

  • En todos los procesadores modernos, se puede asumir que las lecturas y escrituras de tipos nativos naturalmente alineados son atómicas siempre que:

    • 1:El bus de memoria es al menos tan ancho como el tipo que se lee o escribe.
    • 2:La CPU lee y escribe estos tipos en una sola transacción de bus, lo que hace imposible que otros subprocesos los vean en un estado a medio completar.
  • En x86 y X64 no hay garantía de que las lecturas y escrituras de más de ocho bytes sean atómicas.

  • Los proveedores de procesadores definen las operaciones atómicas para cada procesador en un Manual del desarrollador de software
  • En procesadores únicos/sistemas de un solo núcleo es posible utilizar técnicas de bloqueo estándar para evitar que se interrumpan las instrucciones de la CPU, pero esto puede resultar ineficaz.
  • Deshabilitar las interrupciones es otra solución más eficiente, si es posible
  • En sistemas multiprocesador/multinúcleo todavía es posible usar bloqueos, pero simplemente usar una sola instrucción o deshabilitar interrupciones no garantiza el acceso atómico.
  • La atomicidad se puede lograr asegurando que las instrucciones utilizadas afirmen la señal 'LOCK' en el bus para evitar que otros procesadores en el sistema accedan a la memoria al mismo tiempo.

Diferencias de idioma

C#

  • C# garantiza que las operaciones en cualquier tipo de valor integrado que ocupe hasta 4 bytes sean atómicas
  • No se garantiza que las operaciones con tipos de valores que ocupan más de cuatro bytes (doble, largo, etc.) sean atómicas.
  • La CLI garantiza que las lecturas y escrituras de variables de tipo valor que tienen el tamaño (o menor) del tamaño natural del puntero del procesador son atómicas.
    • Por ejemplo, ejecutar C# en un sistema operativo de 64 bits en una versión de 64 bits de CLR realiza lecturas y escrituras de dobles de 64 bits y enteros largos de forma atómica.
  • Creando operaciones atómicas:
    • .NET proporciona la clase entrelazada como parte del espacio de nombres System.Threading
    • La clase entrelazada proporciona operaciones atómicas como incrementar, comparar, intercambiar, etc.
using System.Threading;             

int unsafeCount;                          
int safeCount;                           

unsafeCount++;                              
Interlocked.Increment(ref safeCount);

C++

  • El estándar C++ no garantiza el comportamiento atómico
  • Todas las operaciones de C/C++ se presumen no atómicas a menos que el compilador o el proveedor de hardware especifique lo contrario, incluida la asignación de enteros de 32 bits.
  • Creando operaciones atómicas:
    • La biblioteca de concurrencia de C++ 11 incluye la Biblioteca de operaciones atómicas ()
    • La biblioteca Atomic proporciona tipos atómicos como una clase de plantilla para usar con cualquier tipo que desee.
    • Las operaciones en tipos atómicos son atómicas y, por lo tanto, seguras para subprocesos.

estructura AtomicCounter
{

   std::atomic< int> value;   

   void increment(){                                    
       ++value;                                
   }           

   void decrement(){                                         
       --value;                                                 
   }

   int get(){                                             
       return value.load();                                    
   }      

}

Java

  • Java garantiza que las operaciones en cualquier tipo de valor incorporado que ocupe hasta 4 bytes sean atómicas
  • También se garantiza que las asignaciones a posiciones largas y dobles volátiles serán atómicas.
  • Java proporciona un pequeño conjunto de herramientas de clases que admiten programación segura para subprocesos sin bloqueo en variables individuales a través de java.util.concurrent.atomic
  • Esto proporciona operaciones atómicas sin bloqueo basadas en primitivas de hardware atómico de bajo nivel, como comparar e intercambiar (CAS), también llamado comparar y configurar:
    • Formulario CAS: booleano compareAndSet (valor esperado, valoractualizado);
      • Este método establece atómicamente una variable para updateValue si actualmente contiene el valor esperado, informando que es verdadero en caso de éxito.
import java.util.concurrent.atomic.AtomicInteger;

public class Counter
{
     private AtomicInteger value= new AtomicInteger();

     public int increment(){
         return value.incrementAndGet();  
     }

     public int getValue(){
         return value.get();
     }
}

Fuentes
http://www.evernote.com/shard/s10/sh/c2735e95-85ae-4d8c-a615-52aadc305335/99de177ac05dc8635fb42e4e6121f1d2

La atomicidad sólo puede ser garantizada por el sistema operativo. El sistema operativo utiliza las características del procesador subyacentes para lograr esto.

Así que la creación de su propia función TestAndSet es imposible. (Aunque no estoy seguro de si se podría utilizar un fragmento de línea ASM, y utilizar la tecla de acceso directo TestAndSet (Podría ser que esta afirmación sólo puede hacerse con privilegios del sistema operativo))

EDIT: De acuerdo a los siguientes comentarios de este post, haciendo su propia función 'bittestandset' usando una directiva ASM es directamente posible (en Intel x86). Sin embargo, si estos trucos también trabajan en otros procesadores no está claro.

Me atengo a mi punto: si usted quiere hacer las cosas atmoic, utilizar las funciones del sistema operativo y no lo hace usted mismo

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