relacionales operadores operacion logicos izquierda desplazamiento con asignación asignacion aritmeticos c++ multithreading thread-safety communication visual-c++

c++ - operacion - operadores logicos en c



¿es el operador de asignación ''='' atómico? (4)

En todos los procesadores modernos, puede suponer que las lecturas y escrituras de tipos nativos alineados naturalmente son atómicas. Siempre que el bus de memoria sea al menos tan ancho como el tipo que se lee o escribe, la CPU lee y escribe estos tipos en una sola transacción de bus, lo que hace que otros hilos no puedan verlos en un estado medio completo. En x86 y x64, no hay garantía de que las lecturas y escrituras de más de ocho bytes sean atómicas. Esto significa que las lecturas y escrituras de 16 bytes de los registros de la extensión SIMD de transmisión (SSE) y las operaciones de cadena, pueden no ser atómicas.

No se garantiza que sean atómicas las lecturas y las escrituras de tipos que no están alineados naturalmente, por ejemplo, escribir DWORD que cruzan los límites de cuatro bytes. La CPU puede tener que hacer estas lecturas y escrituras como múltiples transacciones de bus, lo que podría permitir que otro subproceso modifique o vea los datos en el medio de la lectura o escritura.

Estoy implementando Inter-Thread Communication usando una variable global.

//global var volatile bool is_true = true; //thread 1 void thread_1() { while(1){ int rint = rand() % 10; if(is_true) { cout << "thread_1: "<< rint <<endl; //thread_1 prints some stuff if(rint == 3) is_true = false; //here, tells thread_2 to start printing stuff } } } //thread 2 void thread_2() { while(1){ int rint = rand() % 10; if(! is_true) { //if is_true == false cout << "thread_1: "<< rint <<endl; //thread_2 prints some stuff if(rint == 7) //7 is_true = true; //here, tells thread_1 to start printing stuff } } } int main() { HANDLE t1 = CreateThread(0,0, thread_1, 0,0,0); HANDLE t2 = CreateThread(0,0, thread_2, 0,0,0); Sleep(9999999); return 0; }

Pregunta

En el código anterior, uso un volatile bool is_true var volatile bool is_true para cambiar la impresión entre thread_1 y thread_2.

Me pregunto si es seguro para subprocesos usar la operación de asignación aquí .


La seguridad de subprocesos de este fragmento de código no depende de la atomicidad de la asignación. Ambas rutinas de subprocesos funcionan estrictamente a su vez. No hay una condición de carrera: thread_1 arrojará material hasta obtener cierto número aleatorio después del cual saldrá de la "sección de salida" y dejará que el otro hilo funcione en él. Sin embargo, hay un par de cosas que vale la pena mencionar:

  • La función rand () puede no ser segura para subprocesos (no es el problema en el código dado aquí)
  • no debería usar la función CreateThread () de Win32, especialmente cuando está utilizando las funciones de CRT libraly que (potencialmente) utilizan variables globales. Use _beginthreadex () en su lugar.

No se garantiza que este código sea seguro para subprocesos en Win32, ya que Win32 garantiza la atomicidad solo para valores de 4 bytes y de tamaño de puntero correctamente alineados. bool no está garantizado para ser uno de esos tipos. (Por lo general, es un tipo de 1 byte).

Para aquellos que exigen un ejemplo real de cómo esto podría fallar:

Supongamos que bool es un tipo de 1 byte. Supongamos también que su variable is_true pasa a estar almacenada adyacente a otra variable bool (llamémosle other_bool ), de modo que ambos comparten la misma línea de 4 bytes. Para ser más concretos, digamos que is_true está en la dirección 0x1000 y other_bool está en la dirección 0x1001. Supongamos que ambos valores son inicialmente false , y un hilo decide actualizar is_true al mismo tiempo que otro hilo intenta actualizar other_bool . La siguiente secuencia de operaciones puede ocurrir:

  • El subproceso 1 se prepara para establecer is_true en true cargando el valor de 4 bytes que contiene is_true y other_bool . El hilo 1 dice 0x00000000.
  • El subproceso 2 se prepara para establecer other_bool en true cargando el valor de 4 bytes que contiene is_true y other_bool . El hilo 2 dice 0x00000000.
  • El subproceso 1 actualiza el byte en el valor de 4 bytes correspondiente a is_true , produciendo 0x00000001.
  • El subproceso 2 actualiza el byte en el valor de 4 bytes correspondiente a other_bool , produciendo 0x00000100.
  • El subproceso 1 almacena el valor actualizado en la memoria. is_true ahora es true y other_bool ahora es false .
  • El subproceso 2 almacena el valor actualizado en la memoria. is_true ahora es false y other_bool ahora es true .

Observe que al final de esta secuencia, la actualización de is_true se perdió, porque fue sobrescrita por el subproceso 2, que capturó un valor antiguo de is_true .

Ocurre que x86 es muy indulgente con este tipo de error porque admite actualizaciones byte-granulares y tiene un modelo de memoria muy ajustado. Otros procesadores Win32 no son tan indulgentes. Los chips RISC, por ejemplo, a menudo no admiten actualizaciones byte-granulares, e incluso si lo hacen, generalmente tienen modelos de memoria muy débiles.


no, no es ... necesitas usar una primitiva de bloqueo de algún tipo. Dependiendo de la plataforma, puede usar los impulsores, o si va a windows nativos, algo así como InterlockedCompareExchange.

De hecho, en su situación, puede usar algunos de los mecanismos de seguridad de subprocesos para que pueda ''señalar'' su otro hilo para comenzar a hacer lo que desee.