Articulo de referencia

Seguridad de los hilos

En la programación informática multihilo , una función es segura para hilos cuando puede ser invocada o accedida concurrentemente por múltiples hilos sin causar comportamientos ...

En la programación informática multihilo , una función es segura para hilos cuando puede ser invocada o accedida concurrentemente por múltiples hilos sin causar comportamientos inesperados, condiciones de carrera o corrupción de datos . [ 1 ] [ 2 ] Al igual que en el contexto multihilo donde un programa ejecuta varios hilos simultáneamente en un espacio de direcciones compartido y cada uno de esos hilos tiene acceso a la memoria de todos los demás hilos , las funciones seguras para hilos deben garantizar que todos esos hilos se comporten correctamente y cumplan con sus especificaciones de diseño sin interacciones no deseadas. [ 3 ]

Existen diversas estrategias para crear estructuras de datos seguras para subprocesos. [ 3 ]

Niveles de seguridad de los hilos

Los distintos proveedores utilizan una terminología ligeramente diferente para la seguridad de las roscas, [ 4 ] pero la terminología más utilizada para la seguridad de las roscas es: [ 2 ]

  • No es seguro para subprocesos : No se debe acceder a las estructuras de datos simultáneamente por diferentes subprocesos.
  • Seguridad para subprocesos, serialización : Utiliza un único mutex para todos los recursos para garantizar que el subproceso esté libre de condiciones de carrera cuando varios subprocesos acceden a esos recursos simultáneamente.
  • Seguridad para subprocesos y multiprocesos : utiliza un mutex para cada recurso para garantizar que el subproceso esté libre de condiciones de carrera cuando varios subprocesos acceden a esos recursos simultáneamente.

Las garantías de seguridad de subprocesos suelen incluir medidas de diseño para prevenir o limitar el riesgo de distintos tipos de interbloqueos , así como optimizaciones para maximizar el rendimiento concurrente. Sin embargo, no siempre es posible garantizar la ausencia total de interbloqueos, ya que estos pueden ser causados ​​por funciones de devolución de llamada y por la violación de la arquitectura por capas, independientemente de la propia biblioteca.

Las bibliotecas de software pueden proporcionar ciertas garantías de seguridad para subprocesos. [ 5 ] Por ejemplo, se puede garantizar la seguridad para subprocesos en lecturas concurrentes, pero no así en escrituras concurrentes. Que un programa que utilice dicha biblioteca sea seguro para subprocesos depende de si la utiliza de forma coherente con esas garantías.

Enfoques de implementación

A continuación se enumeran dos clases de enfoques para evitar condiciones de carrera y lograr la seguridad de los hilos.

La primera clase de enfoques se centra en evitar el estado compartido e incluye:

Reentrada [ 6 ]
Escribir código de forma que pueda ser ejecutado parcialmente por un hilo, ejecutado por el mismo hilo o ejecutado simultáneamente por otro hilo, y que la ejecución original se complete correctamente. Esto requiere guardar la información de estado en variables locales para cada ejecución, generalmente en una pila, en lugar de en variables estáticas o globales u otro estado no local. Todos los estados no locales deben ser accesibles mediante operaciones atómicas y las estructuras de datos deben ser reentrantes.
Almacenamiento local de subprocesos
Las variables están localizadas de forma que cada hilo tenga su propia copia privada. Estas variables conservan sus valores entre subrutinas y otros límites de código, y son seguras para hilos, ya que son locales a cada hilo, aunque el código que accede a ellas pueda ser ejecutado simultáneamente por otro hilo.
objetos inmutables
El estado de un objeto no puede modificarse tras su construcción. Esto implica que solo se comparten datos de solo lectura y que se garantiza la seguridad inherente de los hilos. Las operaciones mutables (no constantes) pueden implementarse de forma que creen nuevos objetos en lugar de modificar los existentes. Este enfoque es característico de la programación funcional y también lo utilizan las implementaciones de cadenas en Java, C# y Python . (Véase Objeto inmutable ).

La segunda clase de enfoques está relacionada con la sincronización y se utiliza en situaciones donde no se puede evitar el estado compartido:

Exclusión mutua
El acceso a los datos compartidos se serializa mediante mecanismos que garantizan que solo un hilo lea o escriba en ellos a la vez. La incorporación de la exclusión mutua debe planificarse cuidadosamente, ya que un uso inadecuado puede provocar efectos secundarios como interbloqueos , bloqueos permanentes y falta de recursos .
Operaciones atómicas
Se accede a los datos compartidos mediante operaciones atómicas que no pueden ser interrumpidas por otros hilos. Esto generalmente requiere el uso de instrucciones especiales en lenguaje máquina , que pueden estar disponibles en una biblioteca de tiempo de ejecución . Dado que las operaciones son atómicas, los datos compartidos siempre se mantienen en un estado válido, independientemente de cómo accedan a ellos otros hilos. Las operaciones atómicas constituyen la base de muchos mecanismos de bloqueo de hilos y se utilizan para implementar primitivas de exclusión mutua.

Ejemplos

En el siguiente fragmento de código Java , la palabra clave ` synchronized` de Java hace que el método sea seguro para subprocesos:

clase Contador { int privado i = 0 ;public synchronized void inc () { i ++ ; } }

En el lenguaje de programación C , cada hilo tiene su propia pila. Sin embargo, una variable estática no se mantiene en la pila; todos los hilos comparten el acceso simultáneo a ella. Si varios hilos se superponen al ejecutar la misma función, es posible que un hilo modifique una variable estática mientras otro la está comprobando. Este error lógico , difícil de diagnosticar , que suele compilar y ejecutarse correctamente, se denomina condición de carrera . Una forma común de evitarlo es usar otra variable compartida como "bloqueo" o " mutex" ( exclusión mutua ) .

En el siguiente fragmento de código C que llama a las cabeceras POSIX , la función es segura para subprocesos, pero no reentrante:

#include <pthread.h>int incrementCounter () { static int counter = 0 ; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER ;// Solo se permite que un hilo incremente a la vez pthread_mutex_lock ( & mutex );++ contador ;// Almacenar el valor antes de que otros hilos lo incrementen aún más int resultado = contador ;pthread_mutex_unlock ( & mutex );devolver resultado ; }

En el ejemplo anterior, la función increment_counterpuede ser llamada por diferentes hilos sin problema, ya que se utiliza un mutex para sincronizar todo el acceso a la countervariable compartida. Sin embargo, si la función se utiliza en un manejador de interrupciones reentrante y se produce una segunda interrupción mientras el mutex está bloqueado, la segunda rutina se quedará bloqueada indefinidamente. Dado que el servicio de interrupciones puede deshabilitar otras interrupciones, todo el sistema podría verse afectado.

La misma función puede implementarse para que sea segura para subprocesos y reentrante utilizando las operaciones atómicas sin bloqueo , que se introdujeron en C++11 :

importar std ;usando std :: atomic ;int incrementCounter () { static atomic < int > counter ( 0 );// Se garantiza que el incremento se realizará de forma atómica int resultado = ++ contador ;devolver resultado ; }

Véase también

Referencias

  1. Kerrisk, Michael (2010). La interfaz de programación de Linux . No Starch Press . pág.  699, "Capítulo 31: HILOS: SEGURIDAD DE HILOS Y ALMACENAMIENTO POR HILO"{{cite book}}: CS1 mantenimiento: postscript ( enlace )
  2. 1 2 Oracle (01/11/2010). "Oracle: Seguridad de subprocesos" . Docs.oracle.com . Consultado el 16/10/2013. "Un procedimiento es seguro para subprocesos cuando es lógicamente correcto al ser ejecutado simultáneamente por varios subprocesos"; "3 niveles de seguridad para subprocesos".{{cite web}}: CS1 mantenimiento: postscript ( enlace )
  3. 1 2 "Guía de programación multihilo: Capítulo 7 Interfaces seguras e inseguras" . Docs Oracle . Oracle . Noviembre de 2020. Consultado el 30 de abril de 2024 ; "Seguridad de subprocesos"{{cite web}}: CS1 mantenimiento: postscript ( enlace )
  4. "Clasificaciones de seguridad de subprocesos de API" . IBM. 11 de abril de 2023. Consultado el 9 de octubre de 2023 .
  5. "Niveles de seguridad MT para bibliotecas" . Docs Oracle . Consultado el 17 de mayo de 2024 .
  6. "Reentrada y seguridad de subprocesos | Qt 5.6" . Proyecto Qt . Consultado el 20 de abril de 2016 .
  • Expertos en preguntas y respuestas sobre Java (20 de abril de 1999). "Diseño seguro para subprocesos (20/4/99)" . JavaWorld.com . Consultado el 22 de enero de 2012 .
  • TutorialsDesk (30 de septiembre de 2014). "Tutorial sobre sincronización y seguridad de subprocesos con ejemplos en Java" . TutorialsDesk.com . Consultado el 22 de enero de 2012 .
  • Venners, Bill (1 de agosto de 1998). "Diseño para la seguridad de subprocesos" . JavaWorld.com . Recuperado el 22 de enero de 2012 .
  • Suess, Michael (15 de octubre de 2006). "Una breve guía para dominar la seguridad de los hilos" . Thinking Parallel . Recuperado el 22 de enero de 2012 .