stringtranslate.com

Almacenamiento local de subprocesos

En programación de computadoras , el almacenamiento local de subprocesos ( TLS ) es un método de administración de memoria que utiliza memoria estática o global local para un subproceso . El concepto permite el almacenamiento de datos que parecen ser globales en un sistema con subprocesos separados.

Muchos sistemas imponen restricciones sobre el tamaño del bloque de memoria local del subproceso; de hecho, a menudo son límites bastante estrictos. Por otro lado, si un sistema puede proporcionar al menos una variable local de subproceso del tamaño de una dirección de memoria (puntero), entonces esto permite el uso de bloques de memoria de tamaño arbitrario de manera local de subproceso, asignando dicho bloque de memoria dinámicamente y almacenándolo. la dirección de memoria de ese bloque en la variable local del subproceso. En máquinas RISC , la convención de llamada a menudo reserva un registro de puntero de hilo para este uso.

Uso

Si bien generalmente se desaconseja el uso de variables globales en la programación moderna, algunos sistemas operativos más antiguos, como UNIX, se diseñaron originalmente para hardware monoprocesador y, a menudo, utilizan variables globales para almacenar valores importantes. Un ejemplo es el errnoutilizado por muchas funciones de la biblioteca C. En una máquina moderna, donde varios subprocesos pueden estar modificando la errnovariable, una llamada de una función del sistema en un subproceso puede sobrescribir el valor previamente establecido por una llamada de una función del sistema en un subproceso diferente, posiblemente antes de que se pueda seguir el código en ese subproceso diferente. verifique la condición de error. La solución es tener errnouna variable que parezca global, pero que esté físicamente almacenada en un grupo de memoria por subproceso, el almacenamiento local del subproceso.

Un segundo caso de uso sería el de múltiples subprocesos que acumulan información en una variable global. Para evitar una condición de carrera , cada acceso a esta variable global debería estar protegido por un mutex . En cambio, cada subproceso podría acumularse en una variable local del subproceso, eliminando así cualquier posibilidad de una condición de carrera y eliminando así la necesidad de bloqueo. Luego, los subprocesos solo tienen que sincronizar una acumulación final de su propia variable local del subproceso en una única variable global.

implementación de Windows

La función de interfaz de programación de aplicaciones (API) TlsAllocse puede utilizar para obtener un índice de ranura TLS no utilizado ; el índice de ranura TLS se considerará "usado".

Las funciones TlsGetValuey TlsSetValueluego se utilizan para leer y escribir una dirección de memoria en una variable local de subproceso identificada por el índice de ranura TLS . TlsSetValuesólo afecta a la variable del hilo actual. TlsFreeSe puede llamar a la función para liberar el índice de ranura TLS .

Hay un bloque de información de subprocesos Win32 para cada subproceso. Una de las entradas de este bloque es la tabla de almacenamiento local de subprocesos para ese subproceso. [1] TlsAlloc devuelve un índice a esta tabla, único por espacio de direcciones, para cada llamada. Cada subproceso tiene su propia copia de la tabla de almacenamiento local del subproceso. Por lo tanto, cada hilo puede usar TlsSetValue(index) de forma independiente y obtener el valor especificado a través de TlsGetValue(index), porque estos establecen y buscan una entrada en la propia tabla del hilo.

Además de la familia de funciones TlsXxx, los ejecutables de Windows pueden definir una sección que se asigna a una página diferente para cada hilo del proceso en ejecución. A diferencia de los valores TlsXxx, estas páginas pueden contener direcciones arbitrarias y válidas. Estas direcciones, sin embargo, son diferentes para cada subproceso en ejecución y, por lo tanto, no deben pasarse a funciones asincrónicas (que pueden ejecutarse en un subproceso diferente) ni pasarse a código que asume que una dirección virtual es única dentro de todo el proceso. Las secciones TLS se administran mediante paginación de memoria y su tamaño se cuantifica al tamaño de una página (4kB en máquinas x86). Dichas secciones solo pueden definirse dentro de un ejecutable principal de un programa; las DLL no deben contener dichas secciones, porque no se inicializan correctamente cuando se cargan con LoadLibrary.

Implementación de subprocesos

En la API de Pthreads , la memoria local de un subproceso se designa con el término datos específicos del subproceso.

Las funciones pthread_key_createy pthread_key_deletese utilizan respectivamente para crear y eliminar una clave para datos específicos del hilo. El tipo de clave se deja explícitamente opaco y se denomina pthread_key_t. Esta clave puede ser vista por todos los hilos. En cada hilo, la clave se puede asociar con datos específicos del hilo a través de pthread_setspecific. Los datos se pueden recuperar posteriormente usando pthread_getspecific.

Además, pthread_key_createopcionalmente, puede aceptar una función destructora que se llamará automáticamente al salir del subproceso, si los datos específicos del subproceso no son NULL . El destructor recibe el valor asociado a la clave como parámetro para poder realizar acciones de limpieza (cerrar conexiones, liberar memoria, etc.). Incluso cuando se especifica un destructor, el programa aún debe llamar pthread_key_deletepara liberar los datos específicos del subproceso a nivel de proceso (el destructor solo libera los datos locales del subproceso).

Implementación específica del idioma

Además de depender de los programadores para llamar a las funciones API adecuadas, también es posible ampliar el lenguaje de programación para admitir el almacenamiento local de subprocesos (TLS).

C y C++

En C11 , la palabra clave _Thread_localse utiliza para definir variables locales de subproceso. El encabezado <threads.h>, si es compatible, se define thread_localcomo sinónimo de esa palabra clave. Uso de ejemplo:

#include <threads.h> thread_local int foo = 0 ;     

En C11, <threads.h>también se define una serie de funciones para recuperar, cambiar y destruir un almacenamiento local de subprocesos, utilizando nombres que comienzan con tss_. En C23, thread_localella misma se convierte en una palabra clave. [2]

C++ 11 introduce la palabra clave thread_local[3] que se puede utilizar en los siguientes casos

Aparte de eso, varias implementaciones del compilador proporcionan formas específicas de declarar variables locales de subproceso:

En versiones de Windows anteriores a Vista y Server 2008, __declspec(thread)funciona en archivos DLL solo cuando esos archivos están vinculados al ejecutable y no funcionará para aquellos cargados con LoadLibrary() (puede ocurrir una falla de protección o corrupción de datos). [10]

Lisp común y otros dialectos

Common Lisp proporciona una característica llamada variables de ámbito dinámico .

Las variables dinámicas tienen un enlace que es privado para la invocación de una función y todos los elementos secundarios llamados por esa función.

Esta abstracción se asigna naturalmente al almacenamiento específico de subprocesos, y las implementaciones Lisp que proporcionan subprocesos hacen esto. Common Lisp tiene numerosas variables dinámicas estándar, por lo que no se pueden agregar subprocesos de manera sensata a una implementación del lenguaje sin que estas variables tengan una semántica local de subprocesos en enlace dinámico.

Por ejemplo, la variable estándar *print-base*determina la base predeterminada en la que se imprimen los números enteros. Si se anula esta variable, todo el código adjunto imprimirá números enteros en una base alternativa:

;;; la función foo y sus hijos imprimirán ;; en hexadecimal: ( let (( *print-base* 16 )) ( foo ))   

Si las funciones se pueden ejecutar simultáneamente en diferentes subprocesos, este enlace debe ser localmente adecuado al subproceso; de lo contrario, cada subproceso peleará por quién controla una base de impresión global.

D

En D versión 2, todas las variables estáticas y globales son locales de subproceso de forma predeterminada y se declaran con una sintaxis similar a las variables globales y estáticas "normales" en otros lenguajes. Las variables globales deben solicitarse explícitamente utilizando la palabra clave compartida :

int hiloLocal ; // Esta es una variable local de subproceso. compartido int global ; // Esta es una variable global compartida con todos los hilos.     

La palabra clave compartida funciona como clase de almacenamiento y como calificador de tipo : las variables compartidas están sujetas a algunas restricciones que imponen estáticamente la integridad de los datos. [13] Para declarar una variable global "clásica" sin estas restricciones, se debe utilizar la palabra clave insegura __gshared : [14]

__gshared int global ; // Esta es una variable global antigua y sencilla.   

Java

En Java , las variables locales de subproceso se implementan mediante el objeto ThreadLocal de clase . [15] ThreadLocal contiene una variable de tipo T, [15] a la que se puede acceder mediante métodos get/set. Por ejemplo, la variable ThreadLocal que contiene un valor entero se ve así:

privado estático final ThreadLocal <Integer> myThreadLocalInteger = new ThreadLocal <Integer> ( ) ;       

Al menos para Oracle/OpenJDK, esto no utiliza almacenamiento local de subprocesos nativo a pesar de que los subprocesos del sistema operativo se utilizan para otros aspectos de los subprocesos de Java. En cambio, cada objeto Thread almacena un mapa (no seguro para subprocesos) de objetos ThreadLocal con sus valores (a diferencia de cada ThreadLocal que tiene un mapa de objetos Thread con valores e incurre en una sobrecarga de rendimiento). [dieciséis]

Lenguajes .NET: C# y otros

En lenguajes .NET Framework como C# , los campos estáticos se pueden marcar con el atributo ThreadStatic: [17] : 898 

clase FooBar { [ThreadStatic] privado estático int _foo ; }      

En .NET Framework 4.0, la clase System.Threading.ThreadLocal<T> está disponible para asignar y cargar de forma diferida variables locales de subprocesos. [17] : 899 

clase FooBar { sistema estático privado . Enhebrado . ThreadLocal < int > _foo ; }     

También hay una API disponible para asignar dinámicamente variables locales de subprocesos. [17] : 899–890 

Objeto Pascal

En Object Pascal ( Delphi ) o Free Pascal, la palabra clave reservada threadvar se puede usar en lugar de 'var' para declarar variables usando el almacenamiento local de subprocesos.

var mydata_process : entero ; threadvar mydata_threadlocal : entero ;    

C objetivo

En Cocoa , GNUstep y OpenStep , cada NSThreadobjeto tiene un diccionario local de subproceso al que se puede acceder a través del threadDictionarymétodo del subproceso.

NSMutableDictionary * dict = [[ NSThread currentThread ] threadDictionary ]; dict [ @"Una clave" ] = @"Algunos datos" ;       

perla

En Perl, los subprocesos se agregaron en una etapa tardía de la evolución del lenguaje, después de que una gran cantidad de código existente ya estuviera presente en Comprehensive Perl Archive Network (CPAN). Por lo tanto, los subprocesos en Perl de forma predeterminada toman su propio almacenamiento local para todas las variables, para minimizar el impacto de los subprocesos en el código existente que no reconoce subprocesos. En Perl, se puede crear una variable compartida en un hilo utilizando un atributo:

utilizar hilos ; utilizar hilos::compartidos ;  mi $localvar ; mi $sharedvar : compartido ;   

PuroBásico

En PureBasic, las variables de subproceso se declaran con la palabra clave Threaded.

Var roscada

Pitón

En Python versión 2.4 o posterior, la clase local en el módulo de subprocesos se puede utilizar para crear almacenamiento local de subprocesos.

importar  subprocesos mydata  =  subprocesos . local () mis datos . x  =  1

Se pueden crear varias instancias de clase local para almacenar diferentes conjuntos de variables. [18] Por lo tanto, no es un singleton .

Rubí

Ruby puede crear/acceder a variables locales de subprocesos utilizando métodos []=/[]:

Hilo . actual [ :user_id ] = 1  

Óxido

Las variables locales de subproceso se pueden crear en Rust utilizando la thread_local!macro proporcionada por la biblioteca estándar de Rust:

utilice std :: celda :: RefCell ; utilizar std :: hilo ;  hilo_local! ( FOO estático : RefCell <u32> = RefCell :: nuevo ( 1 ) ) ;   FOO . con ( | f | { afirmar_eq! ( * f . pedir prestado (), 1 ); * f . pedir prestado_mut () = 2 ; });      // cada hilo comienza con el valor inicial de 1, aunque este hilo ya cambió su copia del valor local del hilo a 2 let t = thread :: spawn ( move || { FOO . with ( | f | { afirmar_eq! ( * f . pedir prestado (), 1 ); * f . pedir prestado () = 3 } } );             // espera a que se complete el hilo y sal del pánico t . unirse (). desenvolver ();// el hilo original conserva el valor original de 2 a pesar de que el hilo secundario cambia el valor a 3 para ese hilo FOO . con ( | f | { afirmar_eq! ( * f . pedir prestado (), 2 ); });   

Ver también

Referencias

  1. ^ Pietrek, Matt (mayo de 2006). "Bajo el capó". MSDN . Consultado el 6 de abril de 2010 .
  2. ^ "Biblioteca de soporte de simultaneidad: cppreference.com". es.cppreference.com .
  3. ^ Sección 3.7.2 en el estándar C++11
  4. ^ "Información del compilador C específica para la implementación de Sun". Guía del usuario de C Sun Studio 8 . 2004. 2.3 Especificador de almacenamiento local de subprocesos.
  5. ^ "Compiladores XL C/C++". Agosto de 2010. Almacenamiento local de subprocesos (TLS). Archivado desde el original el 11 de abril de 2011.
  6. ^ "Almacenamiento local de subprocesos". Manual CCG 3.3.1 . 2003.
  7. ^ "Notas de la versión LLVM 2.0". 23 de mayo de 2007. Mejoras en llvm-gcc.
  8. ^ "Extensiones del lenguaje Clang: documentación de Clang 3.8". Introducción. Este documento describe las extensiones de idioma proporcionadas por Clang. Además de las extensiones de idiomas enumeradas aquí, Clang pretende admitir una amplia gama de extensiones de GCC. Consulte el manual de GCC para obtener más información sobre estas extensiones.
  9. ^ "Notas de la versión del compilador Intel® C++ 8.1 para Linux para procesadores Intel IA-32 e Itanium®" (PDF) . 2004. Almacenamiento local de subprocesos. Archivado desde el original (PDF) el 19 de enero de 2015.
  10. ^ ab Visual Studio 2003: "Almacenamiento local de subprocesos (TLS)". Documentos de Microsoft . 5 de junio de 2017.
  11. ^ Compilador Intel C++ 10.0 (Windows): almacenamiento local de subprocesos
  12. ^ "Atributos en Clang - documentación de Clang 3.8". hilo.
  13. ^ Alexandrescu, Andrei (6 de julio de 2010). Capítulo 13 - Concurrencia. Informar a TI. pag. 3 . Consultado el 3 de enero de 2014 . {{cite book}}: |website=ignorado ( ayuda )
  14. ^ Bright, Walter (12 de mayo de 2009). "Migrar a compartido". dlang.org . Consultado el 3 de enero de 2014 .
  15. ^ ab Bloch 2018, pag. 151-155, §Artículo 33: Considere contenedores heterogéneos con seguridad de tipos.
  16. ^ "¿Cómo se implementa internamente ThreadLocal de Java?". Desbordamiento de pila . Intercambio de pila . Consultado el 27 de diciembre de 2015 .
  17. ^ abc Albahari 2022.
  18. ^ "cpython/Lib/_threading_local.py en 3.12 · python/cpython". GitHub . Consultado el 25 de octubre de 2023 .

Bibliografía


enlaces externos