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. La API de TLS admite el uso de datos específicos de los subprocesos que se enlazan dinámicamente (en tiempo de ejecución) (TlsAlloc). Win32 y el compilador de Microsoft C++ ahora son compatibles con datos por subproceso que se enlazan estáticamente (en tiempo de carga) además de la implementación existente de la API.

Implementación del compilador para TLS

C++11: el especificador de clase de almacenamiento thread_local es el método recomendado para especificar el almacenamiento local para el subproceso de los objetos y miembros de clase. Para obtener más información, vea el tema sobre Clases de almacenamiento (C++).

MSVC también proporciona un atributo específico de Microsoft, thread, como modificador de clase de almacenamiento extendida. Use la palabra clave __declspec para declarar una variable thread. 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 a thread como a thread_local:

  • El atributo thread se puede aplicar solo a definiciones y declaraciones de clases y datos. No se puede utilizar 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 modificador thread puede especificarse únicamente en elementos de datos con la extensión static. Entre ellos se incluyen objetos de datos globales (tanto static como extern), objetos estáticos locales y miembros de datos estáticos de clases C++. Los objetos de datos automáticos no pueden declararse con el atributo thread. 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;
    }
    
  • Todas las declaraciones y la definición de un objeto local para el subproceso deben especificar el atributo thread. 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 atributo thread no se puede utilizar como modificador de tipo. Por ejemplo, el código siguiente genera un error del compilador:

    char __declspec( thread ) *ch;        // Error
    
  • Dado que se permite declarar objetos C++ que usan el atributo thread, 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 para el subproceso no se considera constante, así como tampoco se considera una expresión constante ninguna expresión en la que intervenga una dirección de ese tipo. En C estándar, el efecto de esto es prohibir el uso de la dirección de una variable local para el 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 a 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. Esto se hace del mismo modo que la construcción de objetos locales para el subproceso. Por ejemplo, el código mostrado anteriormente no genera un error cuando se compila como un archivo de origen de C++. La dirección de una variable local para el subproceso solo será válida mientras exista el subproceso en el que se obtuvo la dirección.

  • C estándar permite la inicialización de un objeto o de una variable con una expresión que contenga una referencia a sí misma, pero solo para objetos cuyo tamaño no sea estático. Aunque, en general, C++ permite esa inicialización dinámica de objetos con una expresión que contenga una referencia a sí misma, este tipo de inicialización no se permite con objetos locales para el 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 expresión sizeof que incluye el objeto que se está inicializando no representa una referencia a sí misma y se permite en C y en C++.

    C++ no permite este tipo de inicialización dinámica de datos de subproceso debido a posibles mejoras futuras en el servicio de almacenamiento local para el subproceso.

  • En sistemas operativos de Windows anteriores a Windows Vista, __declspec( thread ) tiene algunas limitaciones. Si una DLL declara cualquier dato u objeto como __declspec( thread ), puede producir un error de protección si se carga dinámicamente. Después de cargar la DLL mediante LoadLibrary, se producirá un error de sistema siempre que el código haga referencia a los datos de __declspec( thread ). 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 utiliza LoadLibrary, no se puede extender este espacio para albergar las variables locales para el subproceso declaradas con __declspec( thread ). Use las API de TLS, como TlsAlloc, en la DLL para asignar TLS en caso de que la DLL se pueda cargar con LoadLibrary.

Consulte también

Multithreading con C y Win32