En Python específicamente, ¿cómo se comparten las variables entre subprocesos?
Aunque lo he usado threading.Thread
antes, nunca entendí ni vi ejemplos de cómo se compartían las variables. ¿Se comparten entre el hilo principal y los niños o solo entre los niños? ¿Cuándo debería usar el almacenamiento local de subprocesos para evitar este intercambio?
He visto muchas advertencias sobre la sincronización del acceso a datos compartidos entre subprocesos mediante el uso de bloqueos, pero todavía no he visto un buen ejemplo del problema.
¡Gracias por adelantado!
python
multithreading
thread-local
Miguel
fuente
fuente
Respuestas:
En Python, todo se comparte, excepto las variables de función local (porque cada llamada de función obtiene su propio conjunto de locales y los hilos son siempre llamadas de función separadas). E incluso entonces, solo las variables en sí mismas (los nombres que se refieren a objetos) son locales a la función; los objetos mismos son siempre globales y cualquier cosa puede referirse a ellos. El
Thread
objeto de un hilo en particular no es un objeto especial a este respecto. Si almacena elThread
objeto en algún lugar al que puedan acceder todos los subprocesos (como una variable global), todos los subprocesos pueden acceder a eseThread
objeto. Si desea modificar atómicamente cualquier cosa a la que tenga acceso otro hilo, debe protegerlo con un candado. Y, por supuesto, todos los hilos deben compartir este mismo bloqueo, o no sería muy efectivo.Si desea un almacenamiento local de subprocesos real, ahí es donde
threading.local
entra en juego. Los atributos dethreading.local
no se comparten entre subprocesos; cada hilo ve solo los atributos que él mismo colocó allí. Si tiene curiosidad por su implementación, la fuente está en _threading_local.py en la biblioteca estándar.fuente
Considere el siguiente código:
Aquí threading.local () se usa como una forma rápida y sucia de pasar algunos datos de run () a bar () sin cambiar la interfaz de foo ().
Tenga en cuenta que el uso de variables globales no funcionará:
Mientras tanto, si pudiera permitirse pasar estos datos como un argumento de foo (), sería una forma más elegante y bien diseñada:
Pero esto no siempre es posible cuando se usa código de terceros o mal diseñado.
fuente
Puede crear almacenamiento local de subprocesos utilizando
threading.local()
.Los datos almacenados en el tls serán únicos para cada hilo, lo que ayudará a garantizar que no se comparta de forma no intencionada.
fuente
Al igual que en cualquier otro idioma, cada hilo de Python tiene acceso a las mismas variables. No hay distinción entre el 'hilo principal' y los hilos secundarios.
Una diferencia con Python es que el bloqueo de intérprete global significa que solo un hilo puede ejecutar código Python a la vez. Sin embargo, esto no es de mucha ayuda cuando se trata de sincronizar el acceso, ya que todavía se aplican todos los problemas habituales de preferencia, y debe usar primitivas de subprocesamiento como en otros idiomas. Sin embargo, significa que debe reconsiderar si estaba utilizando subprocesos para el rendimiento.
fuente
Puede que me equivoque aquí. Si sabe lo contrario, explique, ya que esto ayudaría a explicar por qué se necesitaría usar el hilo local ().
Esta afirmación parece apagada, no incorrecta: "Si desea modificar atómicamente cualquier cosa a la que tenga acceso otro hilo, debe protegerlo con un candado". Creo que esta afirmación es -> efectivamente <- correcta pero no del todo precisa. Pensé que el término "atómico" significaba que el intérprete de Python creaba un fragmento de código de bytes que no dejaba espacio para una señal de interrupción a la CPU.
Pensé que las operaciones atómicas son fragmentos de código de bytes de Python que no dan acceso a las interrupciones. Las declaraciones de Python como "running = True" son atómicas. No es necesario bloquear la CPU de las interrupciones en este caso (creo). El desglose del código de bytes de Python está a salvo de la interrupción del hilo.
El código Python como "threads_running [5] = True" no es atómico. Aquí hay dos fragmentos de código de bytes de Python; uno para desvincular la lista () de un objeto y otro fragmento de código de bytes para asignar un valor a un objeto, en este caso, un "lugar" en una lista. Se puede generar una interrupción -> entre <- los dos códigos de bytes -> fragmentos <-. Eso es donde pasan cosas malas.
¿Cómo se relaciona el hilo local () con "atómico"? Es por eso que la declaración me parece mal dirigida. Si no, ¿puedes explicarlo?
fuente