¿Las variables thread_local de C ++ 11 son automáticamente estáticas?

85

¿Hay alguna diferencia entre estos dos segmentos de código?

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

y

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

Trasfondo: originalmente tenía un vector ESTÁTICO V (para contener algunos valores intermedios, se borra cada vez que entro a la función) y un programa de un solo subproceso. Quiero convertir el programa en uno de subprocesos múltiples, así que de alguna manera tengo que deshacerme de este modificador estático. ¿Mi idea es convertir cada estática en thread_local y no preocuparme por nada más? ¿Puede este enfoque ser contraproducente?

Zuza
fuente
16
thread_localPara empezar, tener una variable local no tiene sentido ... cada hilo tiene su propia pila de llamadas.
Konrad Rudolph
1
Varias funciones de C se escribieron originalmente para devolver la dirección de variables estáticas o globales. Más tarde se descubrió que esto conducía a errores oscuros cuando se usaba en aplicaciones de subprocesos múltiples (por ejemplo, errno, localtime). Además, a veces es muy perjudicial proteger las variables compartidas con un mutex cuando se llama a una función desde varios subprocesos o tener que pasar un objeto de contexto de subproceso entre muchos objetos y métodos de llamada. Las variables que son locales a un subproceso resuelven estos y otros temas.
edwinc
3
@Konrad Rudolph declara variables locales únicamente como en staticlugar de static thread_localno inicializar una instancia de la variable para cada hilo.
davide
1
@davide Ese no es el punto, ni mío ni del OP. No estamos hablando de staticvs, static thread_localsino más bien de autovs thread_local, usando el significado de antes de C ++ 11 auto(es decir, almacenamiento automático).
Konrad Rudolph
1
Consulte también ¿Cómo definir variables estáticas locales de subprocesos? . Una nota rápida de un abogado de idiomas ... El soporte de Microsoft y TLS cambió en Vista; consulte Almacenamiento local de subprocesos (TLS) . El cambio afecta cosas como Singleton y puede o no aplicarse. Si está utilizando el modelo de software de abondware, probablemente estará bien. Si está contento de admitir varios compiladores y plataformas, es posible que deba prestarle atención.
jww

Respuestas:

92

Según el estándar C ++

Cuando thread_local se aplica a una variable de alcance de bloque, el especificador de clase de almacenamiento estático está implícito si no aparece explícitamente

Entonces significa que esta definición

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

es equivalente a

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

Sin embargo, una variable estática no es lo mismo que una variable thread_local.

1 Todas las variables declaradas con la palabra clave thread_local tienen una duración de almacenamiento de subprocesos. El almacenamiento de estas entidades durará el tiempo que dure el hilo en el que se crearon. Hay un objeto o referencia distinto por hilo, y el uso del nombre declarado se refiere a la entidad asociada con el hilo actual

Para distinguir estas variables, el estándar introduce un nuevo término duración de almacenamiento de subprocesos junto con duración de almacenamiento estático.

Vlad de Moscú
fuente
1
staticy, externpor lo tanto, no implica una clase de almacenamiento, sino solo un enlace para las variables thread_local incluso en ámbitos internos.
Deduplicador
4
@Deduplicator Las variables de alcance del bloque no tienen vinculación. Entonces tu currículum está mal. Como escribí en la publicación, tienen una duración de almacenamiento de subprocesos. De hecho, es lo mismo que la duración del almacenamiento estático pero se aplica a cada subproceso.
Vlad de Moscú
1
Si agrega el extern, está haciendo una declaración, no una definición. ¿Entonces?
Deduplicador
1
@Deduplicator Significa que las definiciones de variables de alcance de bloque no tienen vinculación.
Vlad de Moscú
1
Lo intenté en VS 2013 y grita "Las variables TL no se pueden inicializar dinámicamente". Estoy confundido.
v.oddou
19

Sí, el "almacenamiento local de subprocesos" es muy similar a "global" (o "almacenamiento estático"), solo que en lugar de "duración de todo el programa" tienes "duración de todo el subproceso". Entonces, una variable local de subproceso local de bloque se inicializa la primera vez que el control pasa por su declaración, pero por separado dentro de cada subproceso, y se destruye cuando el subproceso termina.

Kerrek SB
fuente
6

Cuando se usa con thread_local, staticestá implícito en el alcance del bloque (vea la respuesta de @ Vlad), requerido para un miembro de la clase; Supongo que significa enlace para el alcance del espacio de nombres.

Por 9.2 / 6:

Dentro de una definición de clase, un miembro no se declarará con el especificador de clase de almacenamiento thread_local a menos que también se declare estático

Para responder a la pregunta original:

¿Las variables thread_local de C ++ 11 son automáticamente estáticas?

No hay otra opción, excepto para las variables de ámbito de espacio de nombres.

¿Hay alguna diferencia entre estos dos segmentos de código?

No.

vehsakul
fuente
4

El almacenamiento local de subprocesos es estático, pero se comporta de manera bastante diferente al almacenamiento estático simple.

Cuando declaras una variable estática, hay exactamente una instancia de la variable. El compilador / sistema de tiempo de ejecución garantiza que se inicializará en algún momento antes de que realmente lo use, sin especificar exactamente cuándo (algunos detalles se omiten aquí).

C ++ 11 garantiza que esta inicialización será segura para subprocesos, sin embargo, antes de C ++ 11, esta seguridad para subprocesos no estaba garantizada. Por ejemplo

static X * pointer = new X;

podría filtrar instancias de X si más de un subproceso golpea el código de inicialización estático al mismo tiempo.

Cuando declaras un hilo de variable como local, hay potencialmente muchas instancias de la variable. Puede pensar en ellos como si estuvieran en un mapa indexado por thread-id. Eso significa que cada hilo ve su propia copia de la variable.

Una vez más, si se inicializa la variable, el compilador / sistema de tiempo de ejecución garantiza que esta inicialización ocurrirá antes de que se usen los datos y que la inicialización ocurrirá para cada hilo que use la variable. El compilador también garantiza que el inicio será seguro para subprocesos.

Las garantías de seguridad de subprocesos significan que puede haber bastante código detrás de escena para hacer que la variable se comporte de la manera esperada, especialmente considerando que el compilador no tiene forma de saber de antemano exactamente cuántos subprocesos existen en su programa y cuántos de estos tocarán la variable local del hilo.

Dale Wilson
fuente
@Etherealone: ​​Interesante. ¿Qué información en particular? ¿Puede proporcionar una referencia?
Dale Wilson
1
stackoverflow.com/a/8102145/1576556 . El artículo de Wikipedia C ++ 11 lo menciona si mal no recuerdo.
Etherealone
1
Sin embargo, los objetos estáticos se inicializan primero y luego se asignan a la copia. Así que tampoco tengo claro si la inicialización segura para subprocesos incluye la expresión completa. Sin embargo, probablemente lo haga porque de lo contrario no se consideraría seguro para subprocesos.
Etherealone