language agnostic - ¿Cómo se implementan mutexes?
language-agnostic concurrency (5)
¿Son algunas implementaciones mejores que otras para aplicaciones específicas? ¿Hay algo para ganar implementando el tuyo?
Echa un vistazo a la descripción de la instrucción de máquina de Test-and-set en Wikipedia, que alude a cómo se logran las operaciones atómicas a nivel de máquina. Me imagino que la mayoría de las implementaciones de mutex a nivel de lenguaje se basan en el soporte a nivel de máquina como Test-and-set.
Sobre la base de la sugerencia de test-and-set
Adamski, también debe considerar el concepto de "mutexes rápidos de espacio de usuario" o futexes .
Los futex tienen la propiedad deseable de que no requieren una llamada al sistema kernel en los casos comunes de bloqueo o desbloqueo de un mutex no previsto. En estos casos, el código de modo de usuario utiliza con éxito una operación atómica de comparación y de intercambio (CAS) para bloquear o desbloquear el mutex.
Si CAS falla, se contenta el mutex y se debe usar una llamada al sistema kernel - sys_futex
en Linux - para esperar el mutex (en el caso de bloqueo) o para activar otros hilos (en el caso de desbloqueo).
Si realmente quiere implementar esto usted mismo, asegúrese de leer también el paper Ulrich Drepper.
Un mutex preferiblemente se ejecuta en el núcleo del sistema operativo, manteniendo la cantidad de código a su alrededor lo más corto posible, por lo que puede evitar el corte mientras se cambia la tarea a otro proceso. La implementación exacta es, por lo tanto, un poco secreta. Aunque no es complejo. Básicamente es un objeto que tiene un campo booleano, que obtiene y establece.
- Al usar un contador, puede convertirse en un semáforo.
- Un mutex es el punto de partida para una sección crítica, que utiliza un mutex internamente para ver si puede ingresar una sección de código. Si el mutex es libre, establece el mutex y ejecuta el código, solo para liberar el mutex cuando termine. Cuando una sección crítica detecta que un mutex está bloqueado, puede esperar a que se libere el mutex.
Alrededor de la lógica mutex básica hay envoltorios para envolverla en un objeto ... Luego, hay más objetos envoltorios para que estén disponibles fuera del kernel. Y luego otra envoltura para que esté disponible en .NET. Y luego, varios programadores escribirán su propio código de envoltura alrededor de esto para sus propias necesidades lógicas. Los envoltorios alrededor de los envoltorios realmente los hacen un territorio oscuro.
Ahora, con este conocimiento básico sobre las partes internas de mutexes, todo lo que espero es que vayas a usar una implementación que dependa del kernel y el hardware que se encuentra debajo. Estos serían los más confiables. (Si el hardware admite esto.) Si el mutex que está utilizando no funciona en este kernel / nivel de hardware, puede ser confiable pero le aconsejo que no lo use, a menos que no haya otra alternativa.
Por lo que sé, Windows, Linux y .NET usarán mutexes a nivel kernel / hardware.
La página de Wikipedia a la que he vinculado explica más sobre la lógica interna y las posibles implementaciones. Preferiblemente, un mutex es controlado por el hardware, lo que hace que la obtención / configuración completa del mutex sea un Test-and-set . (Solo para asegurarse de que el sistema no cambie tareas intermedias).
Usé Reflector.NET para descompilar la fuente de System.Threading.ReaderWriterLockSlim
, que se agregó a una versión reciente de .NET framework.
Utiliza principalmente Interlocked.CompareExchange
, Thread.SpinWait
y Thread.Sleep
para lograr la sincronización. Hay algunas instancias EventWaitHandle
(objeto kernel) que se usan en algunas circunstancias.
También se agregó cierta complejidad para admitir la reentrada en un único hilo.
Si estás interesado en esta área y estás trabajando en .NET (o al menos, puedes leerla), entonces puede que te resulte bastante interesante comprobar esta clase.
Interlocked.CompareExchange
es suficiente para implementar spinlocks. Sin embargo, es bastante difícil hacerlo bien. Busque en el blog de Joe Duffy un ejemplo de las sutilezas involucradas.