Me gustaría minimizar la sincronización y escribir código bloqueable cuando sea posible en un proyecto mío. Cuando sea absolutamente necesario, me encantaría sustituir las suspensiones de spinlocks livianas construidas a partir de operaciones atómicas por las cerraduras mutex pthread y win32. Según entiendo, se trata de llamadas al sistema que se encuentran debajo y que podrían provocar un cambio de contexto (que puede ser innecesario para secciones críticas muy rápidas en las que sería preferible girar varias veces).¿Spinlock ligeros construidos a partir de las operaciones atómicas de GCC?
Las operaciones atómicas que me refiero están bien documentados aquí: http://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Atomic-Builtins.html
Aquí se muestra un ejemplo para ilustrar lo que estoy hablando. Imagine un árbol RB con múltiples lectores y escritores posible. RBTree :: exists() es de solo lectura y seguro para subprocesos, RBTree :: insert() requerirá acceso exclusivo de un solo escritor (y ningún lector) para estar seguro. Algunos código:
class IntSetTest
{
private:
unsigned short lock;
RBTree<int>* myset;
public:
// ...
void add_number(int n)
{
// Aquire once locked==false (atomic)
while (__sync_bool_compare_and_swap(&lock, 0, 0xffff) == false);
// Perform a thread-unsafe operation on the set
myset->insert(n);
// Unlock (atomic)
__sync_bool_compare_and_swap(&lock, 0xffff, 0);
}
bool check_number(int n)
{
// Increment once the lock is below 0xffff
u16 savedlock = lock;
while (savedlock == 0xffff || __sync_bool_compare_and_swap(&lock, savedlock, savedlock+1) == false)
savedlock = lock;
// Perform read-only operation
bool exists = tree->exists(n);
// Decrement
savedlock = lock;
while (__sync_bool_compare_and_swap(&lock, savedlock, savedlock-1) == false)
savedlock = lock;
return exists;
}
};
(supongamos que no tiene por qué ser una excepción de fallos)
¿Es este código de hecho flujos seguros? ¿Hay algún pros/contras para esta idea? ¿Algún consejo? ¿El uso de spinlocks como este es una mala idea si los hilos no son verdaderamente concurrentes?
Gracias de antemano. ;)
La respuesta que di en una pregunta similar, http://stackoverflow.com/questions/1919135/critical-sections-that-spin-on-posix/1923218#1923218, probablemente será relevante aquí. –
Su respuesta fue definitivamente relevante para la cuestión del uso de spinlocks en general. Parecen una buena idea para las máquinas smp en su caso típico. ¿La situación del peor de los casos (un escritor que deja de ejecutarse durante la sección crítica) se iguala con el caso más probable de que dos hilos simultáneos intenten insertarse al mismo tiempo? ¿Qué ocurre en un entorno de subprocesamiento híbrido en el que los subprocesos del usuario se asignan a varios subprocesos del kernel equivalentes a la cantidad de procesadores lógicos en la máquina? La peor situación posible sería incluso menos probable entonces; ¿no? – Thomas
No estoy seguro de hasta qué punto la cantidad de subprocesos del kernel afecta la probabilidad de encontrarse con problemas de rendimiento. Es posible que el hilo del escritor haya agotado su intervalo de tiempo entre la entrada y la salida del bloqueo, lo que daría lugar al caso del problema sin importar cuántos hilos del kernel haya. En este punto, señalaré que la operación de inserción de árbol RB es O (log (n)), por lo que cuanto mayor sea el árbol, más probabilidades habrá de que ocurra este problema. Además, es más probable que un árbol más grande cause fallas en la página durante la actualización, lo que también haría que el problema fuera más probable. Evitaría los espirales aquí. –