Home Tecnología Lo que todo programador de sistemas debe saber sobre la concurrencia

Lo que todo programador de sistemas debe saber sobre la concurrencia

41
0

La concurrencia es un aspecto basic de la informática moderna, que permite la utilización eficiente de los recursos y mejora el rendimiento. Sin embargo, introduce complejidades que pueden generar problemas sutiles y difíciles de depurar. Este artículo proporciona una descripción common de los conceptos y herramientas clave que los programadores de sistemas deben comprender para gestionar eficazmente la concurrencia.

Fondo

En el entorno informático precise, los subprocesos se ejecutan simultáneamente, ya sea en un procesador de un solo núcleo o de varios núcleos. Estos subprocesos interactúan compartiendo el estado y garantizar el orden correcto de las operaciones de memoria es essential para que sean correctos. Considere un ejemplo easy en el que un hilo escribe un valor y establece una bandera, mientras que otro hilo espera la bandera antes de leer el valor. Garantizar que el indicador esté establecido después de escribir el valor requiere un manejo cuidadoso de las operaciones de memoria.

int v; bool v_ready = false;

void threadA() {
    v = 42;
    v_ready = true;
}

void threadB() {
    whereas (!v_ready) { /* wait */ }
    const int my_v = v;
    // Do one thing with my_v...
}

Hacer cumplir la ley y el orden

Para evitar carreras de datos y garantizar la corrección, los lenguajes de programación como C y C++ proporcionan operaciones y tipos atómicos. Estas herramientas ayudan a gestionar el orden de las operaciones de memoria entre subprocesos.

std::atomic_int v(0);
std::atomic_bool v_ready(false);

void threadA() {
    v = 42;
    v_ready = true;
}

void threadB() {
    whereas (!v_ready.load()) { /* wait */ }
    const int my_v = v.load();
    // Do one thing with my_v...
}

Atomicidad

Las operaciones atómicas garantizan que las lecturas y escrituras en variables compartidas sean indivisibles. Esto evita lecturas y escrituras irregulares, que pueden ocurrir cuando una variable es mayor que el tamaño de palabra de la máquina.

std::atomic counter;

void increment() {
    counter.fetch_add(1, std::memory_order_relaxed);
}

Operaciones de lectura-modificación-escritura

Las operaciones de lectura, modificación y escritura (RMW) son esenciales para implementar primitivas de sincronización. Las operaciones comunes de RMW incluyen intercambio, comparación e intercambio (CAS) y búsqueda y adición.

std::atomic worth;

int old_value = worth.change(42);
if (worth.compare_exchange_weak(anticipated, desired)) {
    // CAS succeeded
}

Ordenes de memoria

Los ordenamientos de la memoria definen las restricciones sobre cómo el compilador y el {hardware} pueden reordenar las operaciones. Las operaciones secuencialmente consistentes brindan las garantías de pedido más sólidas, asegurando un orden whole único de todas las operaciones.

std::atomic x(0), y(0);

void thread1() {
    x.retailer(1, std::memory_order_seq_cst);
    int val = y.load(std::memory_order_seq_cst);
}

void thread2() {
    y.retailer(1, std::memory_order_seq_cst);
    int val = x.load(std::memory_order_seq_cst);
}

Adquirir y liberar

Los pedidos de adquisición y liberación proporcionan barreras de memoria unidireccionales, lo que garantiza que las operaciones dentro de una sección crítica no se reordenen a través de la barrera.

std::atomic flag(false);
int knowledge;

void producer() {
    knowledge = 42;
    flag.retailer(true, std::memory_order_release);
}

void client() {
    whereas (!flag.load(std::memory_order_acquire)) { /* spin */ }
    // knowledge is now seen
}

Operaciones relajadas

Las operaciones relajadas ofrecen las garantías de pedido más débiles y son útiles cuando no se requiere ningún pedido específico.

std::atomic counter(0);

void increment() {
    counter.fetch_add(1, std::memory_order_relaxed);
}

Uso compartido falso y efectos de caché

Los protocolos de coherencia de caché pueden introducir cuellos de botella en el rendimiento debido a un intercambio falso, donde variables no relacionadas comparten la misma línea de caché.

struct RMLock {
    int readers;
    bool writer_flag;
    char padding[CACHE_LINE_SIZE - sizeof(int) - sizeof(bool)];
};

Fusión volátil y atómica

El unstable La palabra clave no es una herramienta adecuada para la concurrencia. No proporciona las garantías necesarias de ordenamiento y atomicidad.

unstable int shared_var; // Incorrect for concurrency

Conclusión

La concurrencia es un tema complejo, pero comprender los conceptos fundamentales de atomicidad, ordenamiento y sincronización puede ayudar a los programadores de sistemas a escribir código concurrente correcto y eficiente. Al utilizar las herramientas y técnicas adecuadas, los programadores pueden evitar errores comunes y crear sistemas concurrentes sólidos.

Recursos adicionales

En caso de que haya encontrado un error en el texto, envíe un mensaje al autor seleccionando el error y presionando Ctrl-Enter.

Debes iniciar sesión para comentar.