Almacenamiento local para el subproceso (TLS)
El almacenamiento local para el subproceso (TLS) es el método por el que cada subproceso de un determinado proceso con subprocesos puede asignar ubicaciones en las que almacenar los datos específicos de esos subprocesos. Los datos específicos del subproceso enlazados dinámicamente (en tiempo de ejecución) se admiten mediante la API tls (TlsAlloc). Win32 y el compilador de Microsoft C++ ahora admiten datos enlazados estáticamente (tiempo de carga) por subproceso, además de la implementación de API existente.
Implementación del compilador de TLS
C++11: El thread_local especificador de clase de almacenamiento es la manera recomendada de especificar el almacenamiento local de subprocesos para objetos y miembros de clase. Para obtener más información, vea Storage clases (C++).
MSVC también proporciona un atributo específico de Microsoft, thread, como modificador de clase de almacenamiento extendido. Use la palabra __declspec clave para declarar una thread variable. Por ejemplo, el código siguiente declara una variable local de subproceso de entero y la inicializa con un valor:
__declspec( thread ) int tls_i = 1;
Reglas y limitaciones
Deben tenerse en cuenta las siguientes instrucciones cuando se declaran objetos y variables locales para el subproceso enlazados estáticamente. Estas directrices se aplican tanto al subproceso como al thread_local:
El
threadatributo solo se puede aplicar a las declaraciones y definiciones de clase y datos. No se puede usar en declaraciones o definiciones de función. Por ejemplo, el código siguiente genera un error del compilador:__declspec( thread )void func(); // This will generate an error.El
threadmodificador solo se puede especificar en elementos de datos constaticextensión. Esto incluye objetos de datos globales (ystaticextern), objetos estáticos locales y miembros de datos estáticos de clases de C++. Los objetos de datos automáticos no se pueden declarar con elthreadatributo . El código siguiente genera errores del compilador:void func1() { __declspec( thread )int tls_i; // This will generate an error. } int func2(__declspec( thread )int tls_i ) // This will generate an error. { return tls_i; }Las declaraciones y la definición de un objeto local de subproceso deben especificar el
threadatributo . Por ejemplo, el siguiente código genera un error:#define Thread __declspec( thread ) extern int tls_i; // This will generate an error, since the int __declspec( thread )tls_i; // declaration and definition differ.El
threadatributo no se puede usar como modificador de tipo. Por ejemplo, el código siguiente genera un error del compilador:char __declspec( thread ) *ch; // ErrorDado que se permite la declaración de objetos de
threadC++ que usan el atributo , los dos ejemplos siguientes son semánticamente equivalentes:__declspec( thread ) class B { // Code } BObject; // OK--BObject is declared thread local. class B { // Code }; __declspec( thread ) B BObject; // OK--BObject is declared thread local.La dirección de un objeto local de subproceso no se considera constante y cualquier expresión que implique dicha dirección no se considera una expresión constante. En C estándar, el efecto es no permitir el uso de la dirección de una variable local de subproceso como inicializador para un objeto o puntero. Por ejemplo, el compilador de C marca el código siguiente como erróneo:
__declspec( thread ) int tls_i; int *p = &tls_i; //This will generate an error in C.Esta restricción no se aplica en C++. Como C++ permite la inicialización dinámica de todos los objetos, puede inicializar un objeto mediante una expresión que utiliza la dirección de una variable local para el subproceso. Se hace igual que la construcción de objetos locales de subproceso. Por ejemplo, el código mostrado anteriormente no genera un error cuando se compila como un archivo de código fuente de C++. La dirección de una variable local de subproceso solo es válida siempre que el subproceso en el que se tomó la dirección todavía exista.
C estándar permite la inicialización de un objeto o variable con una expresión que implica una referencia a sí misma, pero solo para objetos de extensión no estática. Aunque C++ generalmente permite esta inicialización dinámica de objetos con una expresión que implica una referencia a sí misma, este tipo de inicialización no se permite con objetos locales de subproceso. Por ejemplo:
__declspec( thread )int tls_i = tls_i; // Error in C and C++ int j = j; // OK in C++, error in C __declspec( thread )int tls_i = sizeof( tls_i ) // Legal in C and C++Una
sizeofexpresión que incluye el objeto que se inicializa no representa una referencia a sí misma y está habilitada en C y C++.C++ no permite esta inicialización dinámica de datos de subproceso debido a posibles mejoras futuras en la instalación de almacenamiento local de subprocesos.
En Windows sistemas operativos anteriores a Windows Vista,
__declspec( thread )tiene algunas limitaciones. Si un archivo DLL declara datos u objetos como__declspec( thread ), puede provocar un error de protección si se carga dinámicamente. Una vez cargado el archivo DLL con LoadLibrary, se produce un error del sistema cada vez que el código hace referencia a los__declspec( thread )datos. Como el espacio para variables globales de un subproceso se asigna en tiempo de ejecución, el tamaño de este espacio se basa en un cálculo de los requisitos de la aplicación más los requisitos de todas las DLL que se vinculan estáticamente. Cuando se usaLoadLibrary, no se puede ampliar este espacio para permitir las variables locales del subproceso declaradas con__declspec( thread ). Use las API de TLS, como TlsAlloc, en el archivo DLL para asignar TLS si el archivo DLL podría cargarse conLoadLibrary.