Articulo de referencia

Bloqueo (informática)

En informática , un bloqueo o mutex (de exclusión mutua ) es una primitiva de sincronización que impide que varios hilos de ejecución modifiquen o accedan al estado simultáneame...

En informática , un bloqueo o mutex (de exclusión mutua ) es una primitiva de sincronización que impide que varios hilos de ejecución modifiquen o accedan al estado simultáneamente. Los bloqueos imponen políticas de control de concurrencia de exclusión mutua y, gracias a la variedad de métodos posibles, existen múltiples implementaciones únicas para diferentes aplicaciones.

Tipos

Generalmente, los bloqueos son de tipo consultivo , donde cada hilo coopera adquiriendo el bloqueo antes de acceder a los datos correspondientes. Algunos sistemas también implementan bloqueos obligatorios , donde intentar un acceso no autorizado a un recurso bloqueado genera una excepción en la entidad que intenta acceder.

El tipo de bloqueo más sencillo es el semáforo binario . Este proporciona acceso exclusivo a los datos bloqueados. Otros esquemas también ofrecen acceso compartido para la lectura de datos. Otros modos de acceso ampliamente implementados son el exclusivo, el de exclusión forzosa y el de actualización forzosa.

Otra forma de clasificar los bloqueos es según lo que sucede cuando la estrategia de bloqueo impide el progreso de un hilo. La mayoría de los diseños de bloqueo bloquean la ejecución del hilo que solicita el bloqueo hasta que se le permite acceder al recurso bloqueado. Con un spinlock , el hilo simplemente espera ("gira") hasta que el bloqueo esté disponible. Esto es eficiente si los hilos se bloquean por un corto tiempo, ya que evita la sobrecarga de la reprogramación de procesos del sistema operativo. Es ineficiente si el bloqueo se mantiene durante mucho tiempo o si el progreso del hilo que lo mantiene depende de la expropiación del hilo bloqueado.

Los bloqueos suelen requerir soporte de hardware para una implementación eficiente. Este soporte generalmente se presenta en forma de una o más instrucciones atómicas , como " test-and-set ", " fetch-and-add " o " compare-and-swap ". Estas instrucciones permiten que un solo proceso compruebe si el bloqueo está libre y, de ser así, lo adquiera en una única operación atómica.

Las arquitecturas de uniprocesador ofrecen la opción de usar secuencias de instrucciones ininterrumpibles —mediante instrucciones especiales o prefijos de instrucciones para deshabilitar temporalmente las interrupciones— , pero esta técnica no funciona en sistemas multiprocesador con memoria compartida. La correcta implementación de bloqueos en un entorno multiprocesador puede requerir un soporte de hardware o software bastante complejo, con importantes problemas de sincronización .

La razón por la que se requiere una operación atómica es la concurrencia, donde más de una tarea ejecuta la misma lógica. Por ejemplo, considere el siguiente código C :

if ( lock == 0 ) { // Si el bloqueo está libre, establecerlo lock = pid ; }

El ejemplo anterior no garantiza que la tarea tenga el bloqueo, ya que más de una tarea puede estar probándolo simultáneamente. Dado que ambas tareas detectarán que el bloqueo está libre, intentarán establecerlo, sin saber que la otra también lo está haciendo. Los algoritmos de Dekker o Peterson son posibles alternativas si no se dispone de operaciones de bloqueo atómico.

El uso descuidado de los bloqueos puede provocar interbloqueos o bloqueos permanentes . Existen diversas estrategias para evitar o superar estos bloqueos, tanto en la fase de diseño como en la de ejecución . (La estrategia más común consiste en estandarizar las secuencias de adquisición de bloqueos para que las combinaciones de bloqueos interdependientes se adquieran siempre en un orden en cascada definido específicamente).

Algunos lenguajes admiten bloqueos sintácticamente. A continuación se muestra un ejemplo en C# :

espacio de nombres Wikipedia.Ejemplos ;usando System.Threading ;public class Account // Este es un monitor de una cuenta { // Use `object` en versiones anteriores a C# 13 private readonly Lock _balanceLock = new (); private decimal _balance = 0 ;public void Deposit ( decimal amount ) { // Solo un hilo a la vez puede ejecutar esta instrucción. lock ( _balanceLock ) { _balance += amount ; } }public void Retirar ( cantidad decimal ) { // Solo un hilo a la vez puede ejecutar esta instrucción. lock ( _balanceLock ) { _balance -= cantidad ; } } }

C# introdujo System.Threading.Lock en C# 13 en .NET 9.

El código lock(this)puede generar problemas si la instancia es accesible públicamente. [ 1 ]

Al igual que en Java , C# también puede sincronizar métodos completos mediante el atributo MethodImplOptions.Synchronized . [ 2 ] [ 3 ]

[MethodImpl(MethodImplOptions.Synchronized)] public void SomeMethod () { // hacer algo }

Granularidad

Antes de adentrarse en la granularidad de los bloqueos, es necesario comprender tres conceptos relacionados con ellos:

  • Sobrecarga de bloqueo : los recursos adicionales para usar bloqueos, como el espacio de memoria asignado para los bloqueos, el tiempo de CPU para inicializar y destruir bloqueos, y el tiempo para adquirir o liberar bloqueos. Cuantos más bloqueos use un programa, mayor será la sobrecarga asociada a su uso;
  • Contención de bloqueo : esto ocurre cuando un proceso o hilo intenta adquirir un bloqueo que posee otro proceso o hilo. Cuanto más granulares sean los bloqueos disponibles, menos probable será que un proceso o hilo solicite un bloqueo que posee el otro. (Por ejemplo, bloquear una fila en lugar de toda la tabla, o bloquear una celda en lugar de toda la fila).
  • Interbloqueo : situación en la que al menos dos tareas esperan a que la otra tarea libere un bloqueo. A menos que se haga algo, las dos tareas esperarán indefinidamente.

Al elegir el número de bloqueos en la sincronización, existe una compensación entre la disminución de la sobrecarga de bloqueo y la disminución de la contención de bloqueos.

Una propiedad importante de un bloqueo es su granularidad . La granularidad es una medida de la cantidad de datos que protege el bloqueo. En general, elegir una granularidad gruesa (un número pequeño de bloqueos, cada uno protegiendo un segmento grande de datos) resulta en una menor sobrecarga de bloqueo cuando un solo proceso accede a los datos protegidos, pero un peor rendimiento cuando varios procesos se ejecutan concurrentemente. Esto se debe a una mayor contención de bloqueos . Cuanto más grueso sea el bloqueo, mayor será la probabilidad de que impida que un proceso no relacionado continúe. Por el contrario, usar una granularidad fina (un mayor número de bloqueos, cada uno protegiendo una cantidad relativamente pequeña de datos) aumenta la sobrecarga de los bloqueos en sí, pero reduce la contención de bloqueos. El bloqueo granular, donde cada proceso debe mantener múltiples bloqueos de un conjunto común de bloqueos, puede crear dependencias de bloqueo sutiles. Esta sutileza puede aumentar la probabilidad de que un programador introduzca un interbloqueo sin darse cuenta .

En un sistema de gestión de bases de datos , por ejemplo, un bloqueo puede proteger, en orden decreciente de granularidad, parte de un campo, un campo, un registro, una página de datos o una tabla completa. La granularidad gruesa, como el uso de bloqueos de tabla, suele ofrecer el mejor rendimiento para un solo usuario, mientras que la granularidad fina, como los bloqueos de registro, suele ofrecer el mejor rendimiento para varios usuarios.

bloqueos de base de datos

Los bloqueos de base de datos pueden utilizarse para garantizar la sincronización de las transacciones. Por ejemplo, al procesar transacciones de forma concurrente (intercalando transacciones), el uso de bloqueos de dos fases asegura que la ejecución concurrente de la transacción sea equivalente a un orden secuencial de la misma. Sin embargo, los interbloqueos se convierten en un efecto secundario indeseado del bloqueo en las bases de datos. Los interbloqueos se previenen predeterminando el orden de bloqueo entre transacciones o se detectan mediante grafos de espera . Una alternativa al bloqueo para la sincronización de la base de datos, evitando los interbloqueos, consiste en el uso de marcas de tiempo globales totalmente ordenadas.

Existen mecanismos para gestionar las acciones de múltiples usuarios concurrentes en una base de datos; el objetivo es evitar la pérdida de actualizaciones y las lecturas incorrectas. Los dos tipos de bloqueo son el bloqueo pesimista y el bloqueo optimista .

  • Bloqueo pesimista : un usuario que lee un registro con la intención de actualizarlo aplica un bloqueo exclusivo para impedir que otros usuarios lo manipulen. Esto significa que nadie más puede manipular ese registro hasta que el usuario libere el bloqueo. La desventaja es que los usuarios pueden quedar bloqueados durante mucho tiempo, lo que ralentiza la respuesta general del sistema y causa frustración.
Dónde usar el bloqueo pesimista: se utiliza principalmente en entornos con alta contención de datos (el grado de solicitudes de los usuarios al sistema de base de datos en un momento dado); donde el costo de proteger los datos mediante bloqueos es menor que el costo de revertir las transacciones si se producen conflictos de concurrencia. La concurrencia pesimista se implementa mejor cuando los tiempos de bloqueo son cortos, como en el procesamiento programático de registros. La concurrencia pesimista requiere una conexión persistente a la base de datos y no es una opción escalable cuando los usuarios interactúan con los datos, ya que los registros pueden permanecer bloqueados durante períodos de tiempo relativamente largos. No es apropiada para el desarrollo de aplicaciones web.
  • Bloqueo optimista : permite que varios usuarios accedan simultáneamente a la base de datos, mientras que el sistema mantiene una copia de la lectura inicial realizada por cada usuario. Cuando un usuario desea actualizar un registro, la aplicación determina si otro usuario lo ha modificado desde su última lectura. Para ello, compara la lectura inicial almacenada en memoria con el registro de la base de datos para verificar cualquier cambio. Cualquier discrepancia entre la lectura inicial y el registro de la base de datos infringe las reglas de concurrencia y, por lo tanto, provoca que el sistema descarte la solicitud de actualización. Se genera un mensaje de error y se solicita al usuario que inicie el proceso de actualización nuevamente. Mejora el rendimiento de la base de datos al reducir la cantidad de bloqueos necesarios, lo que disminuye la carga en el servidor de la base de datos. Funciona de manera eficiente con tablas que requieren actualizaciones limitadas, ya que ningún usuario queda bloqueado. Sin embargo, algunas actualizaciones pueden fallar. La desventaja son los fallos constantes en las actualizaciones debido al alto volumen de solicitudes de actualización de múltiples usuarios concurrentes, lo que puede resultar frustrante para los usuarios.
Dónde usar el bloqueo optimista: es apropiado en entornos con baja contención de datos o donde se requiere acceso de solo lectura. La concurrencia optimista se usa ampliamente en .NET para satisfacer las necesidades de las aplicaciones móviles y sin conexión, [ 4 ] donde bloquear filas de datos durante períodos prolongados sería inviable. Además, mantener bloqueos de registros requiere una conexión persistente al servidor de la base de datos, lo cual no es posible en aplicaciones sin conexión.

Tabla de compatibilidad de cerraduras

Existen diversas variantes y refinamientos de estos tipos principales de bloqueo, con sus respectivas variaciones en el comportamiento de bloqueo. Si un primer bloqueo bloquea a otro, se dice que ambos son incompatibles ; de lo contrario, son compatibles . A menudo, las interacciones de bloqueo entre tipos de bloqueo se presentan en la literatura técnica mediante una tabla de compatibilidad de bloqueos . A continuación, se muestra un ejemplo con los tipos de bloqueo principales más comunes:

  • indica compatibilidad
  • La X indica incompatibilidad, es decir, un caso en el que un bloqueo del primer tipo (en la columna izquierda) sobre un objeto impide que otro bloqueo del segundo tipo (en la fila superior) adquiera el mismo objeto (por otra transacción). Un objeto suele tener una cola de operaciones solicitadas (por transacciones) en espera, cada una con sus respectivos bloqueos. El primer bloqueo bloqueado en la cola se adquiere en cuanto se elimina el bloqueo existente del objeto, y entonces se ejecuta la operación correspondiente. Si un bloqueo en la cola no está bloqueado por ningún bloqueo existente (es posible que existan varios bloqueos compatibles sobre un mismo objeto simultáneamente), se adquiere inmediatamente.

Comentario: En algunas publicaciones, las entradas de la tabla simplemente se marcan como "compatibles" o "incompatibles", o respectivamente "sí" o "no". [ 5 ]

Desventajas

La protección de recursos basada en bloqueos y la sincronización de hilos/procesos presentan muchas desventajas:

  • Contención: algunos subprocesos o procesos deben esperar hasta que se libere un bloqueo (o un conjunto completo de bloqueos). Si uno de los subprocesos que mantiene un bloqueo falla, se detiene, se bloquea o entra en un bucle infinito, otros subprocesos que esperan el bloqueo pueden esperar indefinidamente hasta que se reinicie el equipo .
  • Sobrecarga: el uso de bloqueos añade sobrecarga por cada acceso a un recurso, incluso cuando las probabilidades de colisión son muy bajas. (Sin embargo, cualquier posibilidad de tales colisiones constituye una condición de carrera ).
  • Depuración: los errores asociados con los bloqueos dependen del tiempo y pueden ser muy sutiles y extremadamente difíciles de reproducir, como los interbloqueos .
  • Inestabilidad: el equilibrio óptimo entre la sobrecarga de bloqueo y la contención de bloqueos puede ser específico del dominio del problema (aplicación) y sensible al diseño, la implementación e incluso a cambios arquitectónicos de bajo nivel del sistema. Estos equilibrios pueden variar a lo largo del ciclo de vida de una aplicación y pueden requerir cambios importantes para su actualización (reequilibrio).
  • Componibilidad: los bloqueos solo son componibles (por ejemplo, gestionar múltiples bloqueos concurrentes para eliminar atómicamente el elemento X de la tabla A e insertar X en la tabla B) con un soporte de software relativamente elaborado (que implica una sobrecarga) y una perfecta adhesión de la programación de aplicaciones a convenciones rigurosas.
  • Inversión de prioridad : un hilo o proceso de baja prioridad que mantiene un bloqueo común puede impedir que los hilos o procesos de alta prioridad continúen. La herencia de prioridad puede utilizarse para reducir la duración de la inversión de prioridad. El protocolo de límite de prioridad puede utilizarse en sistemas monoprocesador para minimizar la duración de la inversión de prioridad en el peor de los casos, así como para prevenir el interbloqueo .
  • Convoying : todos los demás subprocesos deben esperar si un subproceso que mantiene un bloqueo se desprograma debido a una interrupción de segmento de tiempo o un fallo de página.

Algunas estrategias de control de concurrencia evitan algunos o todos estos problemas. Por ejemplo, un embudo o la serialización de tokens pueden evitar el mayor problema: los interbloqueos. Entre las alternativas al bloqueo se incluyen métodos de sincronización sin bloqueo , como las técnicas de programación sin bloqueo y la memoria transaccional . Sin embargo, estos métodos alternativos suelen requerir que los mecanismos de bloqueo se implementen a un nivel más fundamental del software operativo. Por lo tanto, es posible que solo liberen a la aplicación de los detalles de la implementación de bloqueos, y que los problemas mencionados anteriormente sigan teniendo que resolverse a un nivel inferior.

En la mayoría de los casos, un bloqueo adecuado depende de que la CPU proporcione un método de sincronización atómica del flujo de instrucciones (por ejemplo, la adición o eliminación de un elemento en una tubería requiere que todas las operaciones simultáneas que necesiten agregar o eliminar otros elementos en la tubería se suspendan durante la manipulación del contenido de la memoria necesaria para agregar o eliminar el elemento específico). Por lo tanto, una aplicación suele ser más robusta cuando reconoce la carga que impone al sistema operativo y es capaz de reconocer adecuadamente la notificación de demandas imposibles.

Falta de capacidad de composición

Uno de los mayores problemas de la programación basada en bloqueos es que "los bloqueos no se componen ": es difícil combinar módulos pequeños y correctos basados ​​en bloqueos en programas más grandes igualmente correctos sin modificar los módulos o al menos conocer sus detalles internos. Simon Peyton Jones (un defensor de la memoria transaccional de software ) da el siguiente ejemplo de una aplicación bancaria: [ 6 ] diseñar una clase Cuenta que permita a múltiples clientes concurrentes depositar o retirar dinero de una cuenta, y dar un algoritmo para transferir dinero de una cuenta a otra.

La solución basada en cerraduras para la primera parte del problema es:

clase Cuenta: miembro balance: Entero miembro mutex: Bloqueo método deposit(n: Entero) mutex.lock() equilibrio ← equilibrio + n mutex.desbloquear() método retirar(n: Entero) depósito(−n)

La segunda parte del problema es mucho más complicada. Una rutina de transferencia que sea correcta para programas secuenciales sería

función transferir(desde: Cuenta, a: Cuenta, importe: Entero) de.retirar(cantidad) depositar(cantidad)

En un programa concurrente, este algoritmo es incorrecto porque cuando un hilo está a mitad de una transferencia , otro podría observar un estado en el que se ha retirado una cantidad de la primera cuenta, pero aún no se ha depositado en la otra: el dinero ha desaparecido del sistema. Este problema solo se puede solucionar por completo bloqueando ambas cuentas antes de modificar cualquiera de ellas, pero entonces los bloqueos deben colocarse según un orden global arbitrario para evitar el interbloqueo.

función transferir(desde: Cuenta, hasta: Cuenta, cantidad: Entero) si desde < hasta // orden arbitrario en los bloqueos desde.bloqueo() bloquear() demás bloquear() desde.bloqueo() de.retirar(cantidad) depositar(cantidad) desde.desbloquear() para.desbloquear()

Esta solución se complica cuando hay más cerraduras involucradas, y la función de transferencia necesita conocer todas las cerraduras, por lo que no pueden ocultarse .

Soporte de idiomas

Los lenguajes de programación varían en su soporte para la sincronización:

  • Ada proporciona objetos protegidos que tienen subprogramas o entradas protegidas visibles [ 7 ] así como encuentros. [ 8 ]
  • El estándar ISO/IEC C proporciona una interfaz de programación de aplicaciones (API) estándar de exclusión mutua (bloqueos) desde C11 , en el encabezado , con varias funciones de manipulación de mutex. [ 9 ]<threads.h>
  • El estándar ISO/IEC C++ actual admite funcionalidades de subprocesos desde C++11 . En particular, proporciona la clase std::mutex, así como varias clases de bloqueo como std::lock_guard, std::unique_lock, y std::scoped_locken el archivo de cabecera <mutex>. [ 11 ]
  • C# proporciona la lockpalabra clave en un hilo para asegurar su acceso exclusivo a un recurso, así como a la System.Threading.Lockclase. [ 13 ]
  • Visual Basic (.NET) proporciona una SyncLockpalabra clave similar a la palabra clave de C# lock.
  • Java proporciona la palabra clave synchronizedpara bloquear bloques de código, métodos u objetos [ 14 ] y bibliotecas que presentan estructuras de datos seguras para la concurrencia. Java también presenta la interfaz java.util.concurrent.locks.Lock. [ 15 ]
  • Objective-C proporciona la palabra clave @synchronized[ 16 ] para poner bloqueos en bloques de código y también proporciona las clases NSLock, [ 17 ] NSRecursiveLock, [ 18 ] y NSConditionLock [ 19 ] junto con el protocolo NSLocking [ 20 ] para bloquear también.
  • PHP proporciona un bloqueo basado en archivos [ 21 ] así como una Mutexclase en la pthreadsextensión. [ 22 ]
  • Python proporciona un mecanismo mutex de bajo nivel con la clase threading.Lock. [ 23 ]
  • El estándar ISO/IEC Fortran (ISO/IEC 1539-1:2010) proporciona el lock_typetipo derivado en el módulo intrínseco iso_fortran_envy las sentencias lock/ unlockdesde Fortran 2008. [ 24 ]
  • Ruby proporciona un objeto mutex de bajo nivel y ninguna palabra clave. [ 25 ]
  • Rust proporciona la estructura std::sync::Mutex<T>[ 26 ] . [ 27 ]
  • El lenguaje ensamblador x86 proporciona un LOCKprefijo en ciertas operaciones para garantizar su atomicidad.
  • Haskell implementa el bloqueo mediante una estructura de datos mutable llamada MVar, que puede estar vacía o contener un valor, normalmente una referencia a un recurso. Un hilo que desea utilizar el recurso "toma" el valor de MVar, dejándolo vacío, y lo devuelve cuando termina. Intentar tomar un recurso de un vacío MVarprovoca que el hilo se bloquee hasta que el recurso esté disponible. [ 28 ] Como alternativa al bloqueo, también existe una implementación de memoria transaccional de software . [ 29 ]
  • Go proporciona un objeto Mutex de bajo nivel , en el paquete syncsync.Mutex de la biblioteca estándar . [ 30 ] Se puede utilizar para bloquear bloques de código, métodos u objetos .

Mutex frente a semáforos

Un mutex es un mecanismo de bloqueo que a veces utiliza la misma implementación básica que un semáforo binario. Sin embargo, difieren en su uso. Si bien un semáforo binario puede denominarse coloquialmente mutex, un mutex propiamente dicho tiene un caso de uso y una definición más específicos, ya que solo la tarea que lo bloqueó puede desbloquearlo. Esta restricción busca solucionar algunos problemas potenciales del uso de semáforos.

  1. Inversión de prioridad : Si el mutex sabe quién lo bloqueó y debe desbloquearlo, es posible promover la prioridad de esa tarea siempre que una tarea de mayor prioridad comience a esperar en el mutex.
  2. Terminación prematura de tareas: Los mutex también pueden proporcionar seguridad contra la eliminación, impidiendo que la tarea que los contiene sea eliminada accidentalmente. (Esto también tiene un coste: si el mutex impide que una tarea sea recuperada, el recolector de basura debe supervisarlo).
  3. Interbloqueo de terminación: Si una tarea que mantiene un mutex finaliza por cualquier motivo, el sistema operativo puede liberar el mutex y notificar a las tareas en espera sobre esta condición.
  4. Interbloqueo recursivo: una tarea puede bloquear un mutex reentrante varias veces, al igual que lo desbloquea un número igual de veces.
  5. Liberación accidental: Se produce un error al liberar el mutex si la tarea que lo libera no es su propietaria.

Véase también

Referencias

  1. "Instrucción lock (Referencia de C#)" . 4 de febrero de 2013.
  2. "ThreadPoolPriority y MethodImplAttribute" . MSDN. p. ?? . Consultado el 22 de noviembre de 2011 . 
  3. "C# desde la perspectiva de un desarrollador Java" . Consultado el 22 de noviembre de 2011 .{{cite web}}: CS1 maint: servicio de archivado obsoleto ( enlace )
  4. "Diseño de componentes de nivel de datos y transferencia de datos a través de niveles" . Microsoft . Agosto de 2002. Archivado del original el 8 de mayo de 2008. Consultado el 30 de mayo de 2008 .
  5. "Protocolo de control de concurrencia basado en bloqueos en DBMS" . GeeksforGeeks . 7 de marzo de 2018. Consultado el 28 de diciembre de 2023 .
  6. Peyton Jones, Simon (2007). "Concurrencia hermosa" (PDF) . En Wilson, Greg; Oram, Andy (eds.). Código hermoso: Programadores líderes explican cómo piensan . O'Reilly.
  7. ISO/IEC 8652:2007. "Unidades protegidas y objetos protegidos" . Manual de referencia de Ada 2005. Consultado el 27 de febrero de 2010. Un objeto protegido proporciona acceso coordinado a datos compartidos, mediante llamadas a sus operaciones protegidas visibles, que pueden ser subprogramas protegidos o entradas protegidas. {{cite book}}: CS1 maint: nombres numéricos: lista de autores ( enlace )
  8. ISO/IEC 8652:2007. "Ejemplo de asignación de tareas y sincronización" . Manual de referencia de Ada 2005. Consultado el 27 de febrero de 2010 . {{cite book}}: CS1 maint: nombres numéricos: lista de autores ( enlace )
  9. cppreference.com. "Encabezado de la biblioteca estándar <threads.h> (C11)" . cppreference.com . cppreference.com . Consultado el 14 de abril de 2026 .
  10. Marshall, Dave (marzo de 1999). "Cerraduras de exclusión mutua" . Recuperado el 30 de mayo de 2008 .
  11. cppreference.com. "Encabezado de la biblioteca estándar <mutex> (C++11)" . cppreference.com . cppreference.com . Consultado el 14 de abril de 2026 .
  12. "Sincronizar" . msdn.microsoft.com . Consultado el 30 de mayo de 2008 .
  13. Microsoft Learn. "Bloquear clase" . learn.microsoft.com . Microsoft Learn . Consultado el 14 de abril de 2026 .
  14. "Sincronización" . Sun Microsystems . Consultado el 30 de mayo de 2008 .
  15. Oracle Corporation (17 de marzo de 2026). "Bloqueo de interfaz" . docs.oracle.com . Documentación de Java SE.
  16. "Apple Threading Reference" . Apple, Inc. Recuperado el 17 de octubre de 2009 .
  17. "Referencia de NSLock" . Apple, Inc. Consultado el 17 de octubre de 2009 .
  18. "Referencia a NSRecursiveLock" . Apple, Inc. Consultado el 17 de octubre de 2009 .
  19. "Referencia a NSConditionLock" . Apple, Inc. Consultado el 17 de octubre de 2009 .
  20. "Referencia del protocolo NSLocking" . Apple, Inc. Consultado el 17 de octubre de 2009 .
  21. "rebaño" .
  22. "La clase Mutex" . Archivado del original el 4 de julio de 2017. Consultado el 29 de diciembre de 2016 .
  23. Lundh, Fredrik (julio de 2007). "Mecanismos de sincronización de hilos en Python" . Archivado del original el 1 de noviembre de 2020. Consultado el 30 de mayo de 2008 .
  24. John Reid (2010). "Coarrays en el próximo estándar Fortran" (PDF) . Consultado el 17 de febrero de 2020 .
  25. "clase Thread::Mutex" .
  26. "std::sync::Mutex - Rust" . doc.rust-lang.org . Consultado el 3 de noviembre de 2020 .
  27. "Concurrencia de estado compartido: el lenguaje de programación Rust" . doc.rust-lang.org . Consultado el 3 de noviembre de 2020 .
  28. Marlow, Simon (agosto de 2013). "Concurrencia básica: hilos y MVars". Programación paralela y concurrente en Haskell . O'Reilly Media . ISBN 9781449335946.
  29. Marlow, Simon (agosto de 2013). «Memoria transaccional de software». Programación paralela y concurrente en Haskell . O'Reilly Media . ISBN 9781449335946.
  30. "sincronizar paquete - sincronizar - pkg.go.dev" . pkg.go.dev . Consultado el 23-11-2021 .
  • Tutorial sobre cerraduras y secciones críticas