La duración del almacenamiento local de subprocesos es un término utilizado para referirse a datos que aparentemente tienen una duración de almacenamiento global o estática (desde el punto de vista de las funciones que lo usan), pero en realidad, hay una copia por subproceso.
Se agrega a la automática actual (existe durante un bloque / función), estática (existe durante la duración del programa) y dinámica (existe en el montón entre asignación y desasignación).
Algo que es local de subprocesos se crea cuando se crea un subproceso y se elimina cuando el subproceso se detiene.
Algunos ejemplos siguen.
Piense en un generador de números aleatorios donde la semilla debe mantenerse por subproceso. Usar una semilla local de hilo significa que cada hilo obtiene su propia secuencia de números aleatorios, independiente de otros hilos.
Si su semilla era una variable local dentro de la función aleatoria, se inicializaría cada vez que la llamara, dándole el mismo número cada vez. Si fuera global, los hilos interferirían entre sí con las secuencias.
Otro ejemplo es algo así como strtok
el estado de tokenización se almacena en una base específica de subproceso. De esa manera, un solo subproceso puede estar seguro de que otros subprocesos no arruinarán sus esfuerzos de tokenización, al tiempo que puede mantener el estado en múltiples llamadas a strtok
, esto básicamente hace que strtok_r
la versión segura para subprocesos sea redundante.
Ambos ejemplos permiten que la variable local de hilo exista dentro de la función que la usa. En el código previamente enhebrado, sería simplemente una variable de duración de almacenamiento estático dentro de la función. Para subprocesos, eso se modifica para la duración del almacenamiento local de subprocesos.
Sin embargo, otro ejemplo sería algo así errno
. No desea que los hilos separados se modifiquen errno
después de que una de sus llamadas falle, pero antes de que pueda verificar la variable, y sin embargo, solo desea una copia por hilo.
Este sitio tiene una descripción razonable de los diferentes especificadores de duración de almacenamiento.
strtok
.strtok
se rompe incluso en un entorno de un solo subproceso.r
significa "reentrante", que no tiene nada que ver con la seguridad del hilo. Es cierto que puede hacer que algunas cosas funcionen de forma segura con subprocesos con almacenamiento local de subprocesos, pero no puede hacer que vuelvan a entrar.strtok
deba llamar a otras funciones.while (something) { char *next = strtok(whatever); someFunction(next); // someFunction calls strtok }
Cuando declaras una variable
thread_local
, cada hilo tiene su propia copia. Cuando se refiere a él por su nombre, se utiliza la copia asociada con el hilo actual. p.ejEste código generará "2349", "3249", "4239", "4329", "2439" o "3429", pero nunca otra cosa. Cada hilo tiene su propia copia de
i
, que se asigna, se incrementa y luego se imprime. El hilo en ejecuciónmain
también tiene su propia copia, que se asigna al principio y luego se deja sin cambios. Estas copias son completamente independientes y cada una tiene una dirección diferente.Es solo el nombre lo que es especial a ese respecto: si toma la dirección de una
thread_local
variable, simplemente tiene un puntero normal a un objeto normal, que puede pasar libremente entre hilos. p.ejDado que la dirección de
i
se pasa a la función de subproceso, la copia dei
pertenencia al subproceso principal se puede asignar aunque seathread_local
. Este programa generará así "42". Si hace esto, debe tener cuidado de que*p
no se acceda después de que el hilo al que pertenece haya salido, de lo contrario, obtendrá un puntero colgante y un comportamiento indefinido como cualquier otro caso en el que se destruye el objeto señalado.thread_local
las variables se inicializan "antes del primer uso", por lo que si nunca son tocadas por un hilo dado, entonces no necesariamente se inicializan. Esto es para permitir que los compiladores eviten construir cadathread_local
variable en el programa para un hilo que sea completamente autónomo y no toque ninguna de ellas. p.ejEn este programa hay 2 hilos: el hilo principal y el hilo creado manualmente. Ninguno de los hilos llama
f
, por lo que elthread_local
objeto nunca se usa. Por lo tanto, no se especifica si el compilador construirá 0, 1 o 2 instancias demy_class
, y la salida puede ser "", "hellohellogoodbyegoodbye" o "hellogoodbye".fuente
g()
llamada al comienzo dethreadFunc
, a continuación, la salida será0304029
o alguna otra permutación de los pares02
,03
y04
. Es decir, aunque se asigna 9i
antes de que se creen los hilos, los hilos obtienen una copia recién construida dei
dóndei=0
. Sii
se le asignathread_local int i = random_integer()
, entonces cada hilo obtiene un nuevo entero aleatorio.02
,03
,04
, pueden existir otras secuencias como020043
El almacenamiento local de subprocesos está en todos los aspectos como el almacenamiento estático (= global), solo que cada subproceso tiene una copia separada del objeto. El tiempo de vida del objeto comienza en el inicio del subproceso (para las variables globales) o en la primera inicialización (para las estadísticas locales de bloque), y finaliza cuando finaliza el subproceso (es decir, cuando
join()
se llama).En consecuencia, solo las variables que también podrían declararse
static
pueden declararse comothread_local
, es decir, variables globales (más precisamente: variables "en el ámbito del espacio de nombres"), miembros de clase estáticos y variables estáticas en bloque (en cuyo casostatic
está implícito).Como ejemplo, suponga que tiene un grupo de subprocesos y desea saber qué tan bien se está equilibrando su carga de trabajo:
Esto imprimiría estadísticas de uso de hilos, por ejemplo, con una implementación como esta:
fuente