2011-12-24 15 views
28

Tengo varias preguntas con respecto a los subprocesos de Python.Qué es un subproceso de python

  1. ¿Es un hilo de Python una implementación de Python o OS?
  2. Cuando uso htop, un script de subprocesos múltiples tiene entradas múltiples: el mismo consumo de memoria, el mismo comando pero un PID diferente. ¿Esto significa que un hilo [de Python] es realmente un tipo especial de proceso? (Sé que hay un ajuste en H TOP para mostrar estos hilos como un solo proceso - Hide userland threads)
  3. Documentation dice:

Un hilo puede ser marcado como un “hilo de utilidad”. La importancia de esta bandera es que todo el programa Python se cierra cuando solo quedan los hilos daemon .

Mi interpretación/comprensión fue: el hilo principal finaliza cuando todos los hilos no daemon se terminan.

Por lo tanto, los subprocesos de daemon python no son parte del programa Python si "todo el programa Python se cierra cuando solo quedan subprocesos de daemon"?

+0

Quieres decir 'threading.Thread' ¿no? –

+0

Sí, lo hago. ¿Hay otros hilos en Python estándar? – warvariuc

+0

Sí, el módulo 'thread' proporciona otra interfaz para threads nativos (pero hey usa la misma implementación nativa). –

Respuesta

24
  1. hilos de Python se implementan utilizando hebras de SO en todas las implementaciones que conozco (C Python, PyPy y Jython). Para cada subproceso de Python, hay una secuencia de sistema operativo subyacente.

  2. Algunos sistemas operativos (Linux es uno de ellos) muestran todos los subprocesos lanzados por el mismo ejecutable en la lista de todos los procesos en ejecución. Este es un detalle de implementación del sistema operativo, no de Python. En algunos otros sistemas operativos, es posible que no vea esos subprocesos al enumerar todos los procesos.

  3. El proceso terminará cuando finalice el último hilo que no sea daemon. En ese punto, todos los hilos daemon serán terminados. Entonces, esos subprocesos son parte de su proceso, pero no impiden que finalicen (mientras que un hilo regular lo evitará). Eso se implementa en Python puro. Un proceso finaliza cuando se llama a la función del sistema _exit (matará a todos los hilos), y cuando el hilo principal finaliza (o se llama al sys.exit), el intérprete de Python verifica si hay otro hilo que no sea daemon en ejecución. Si no hay ninguno, llama al _exit, de lo contrario, espera a que finalicen los subprocesos no daemon.


La bandera hilo de utilidad se implementa en Python puro por el módulo de threading. Cuando se carga el módulo, se crea un objeto Thread para representar el hilo principal, y su método _exitfunc se registra como un gancho atexit.

El código de esta función es:

class _MainThread(Thread): 

    def _exitfunc(self): 
     self._Thread__stop() 
     t = _pickSomeNonDaemonThread() 
     if t: 
      if __debug__: 
       self._note("%s: waiting for other threads", self) 
     while t: 
      t.join() 
      t = _pickSomeNonDaemonThread() 
     if __debug__: 
      self._note("%s: exiting", self) 
     self._Thread__delete() 

Esta función será llamado por el intérprete de Python cuando sys.exit se llama, o cuando el hilo principal termina. Cuando la función retorna, el intérprete llamará a la función del sistema _exit. Y la función terminará, cuando solo hay hilos daemon en ejecución (si hay alguno).

Cuando se llama a la función _exit, el sistema operativo terminará todos los procesos y luego finalizará el proceso. El tiempo de ejecución de Python no llamará a la función _exit hasta que todo el subproceso no-demonio esté listo.

Todos los hilos son parte del proceso.


Mi interpretación/comprensión era: hilo principal termina cuando se terminan todos los hilos no demonio.

Por lo tanto, los subprocesos de daemon de python no son parte del programa de Python si "todo el programa Python se cierra cuando solo quedan subprocesos de daemon"?

Su comprensión es incorrecta. Para el sistema operativo, un proceso se compone de muchos hilos, todos iguales (no hay nada especial sobre el hilo principal para el sistema operativo, excepto que el tiempo de ejecución de C agrega una llamada al _exit al final de la función main). Y el sistema operativo no sabe acerca de los hilos daemon. Esto es puramente un concepto de Python.

El intérprete de Python utiliza el hilo nativo para implementar el hilo de Python, pero tiene que recordar la lista de hilos creados. Y utilizando su gancho atexit, asegura que la función _exit regresa al sistema operativo solo cuando finaliza el último subproceso no-demonio. Al usar "todo el programa Python", la documentación se refiere a todo el proceso.


El siguiente programa puede ayudar a entender la diferencia entre el hilo de utilidad e hilo normal:

import sys 
import time 
import threading 

class WorkerThread(threading.Thread): 

    def run(self): 
     while True: 
      print 'Working hard' 
      time.sleep(0.5) 

def main(args): 
    use_daemon = False 
    for arg in args: 
     if arg == '--use_daemon': 
      use_daemon = True 
    worker = WorkerThread() 
    worker.setDaemon(use_daemon) 
    worker.start() 
    time.sleep(1) 
    sys.exit(0) 

if __name__ == '__main__': 
    main(sys.argv[1:]) 

Si se ejecuta este programa con el '--use_daemon', se verá que el programa sólo se imprima un pequeño número de líneas Working hard. Sin esta bandera, el programa no terminará incluso cuando finalice el hilo principal, y el programa imprimirá líneas Working hard hasta que se mate.

+0

> El proceso terminará cuando finalice el último subproceso no demoníaco. El significado de este indicador es que todo el programa Python sale cuando solo los hilos daemon quedan <¿Cuál es el "programa completo de Python" aquí? Pensé que es el proceso. Pero, ¿cómo puede terminarse el proceso cuando todavía tiene hilos? – warvariuc

+0

Actualicé mi respuesta para explicar cómo se implementa el hilo daemon en Python. –

+0

Gracias por su paciencia. > Cuando se llama a la función '_exit', el SO terminará todos los hilos del proceso y luego terminará el proceso. warvariuc

3
  1. hilos Python son prácticamente una aplicación intérprete, porque el llamado bloqueo intérprete mundial (GIL), incluso si es técnicamente el uso de los mecanismos de roscado a nivel de sistema operativo. On * nix está utilizando los pthreads, pero el GIL lo convierte en un hito híbrido para el paradigma de enhebrado a nivel de aplicación. Por lo tanto, lo verá en los sistemas * nix varias veces en una salida ps/top, pero aún se comporta (en cuanto al rendimiento) como un subproceso implementado por software.

  2. No, usted acaba de ver el tipo de implementación de subproceso subyacente de su sistema operativo. Este tipo de comportamiento está expuesto por * nix subthread-like threading o me he dicho que windows incluso implementa hilos de esta manera.

  3. Cuando su programa se cierra, espera a que todos los hilos terminen también. Si tiene subprocesos, lo que podría posponer la salida indefinidamente, puede ser conveniente marcar esos subprocesos como "demonios" y permitir que su programa termine incluso si esos subprocesos aún se están ejecutando.

material de alguna referencia que podría estar interesado:

+2

1. Esto está mal. Los hilos de Python son hilos del sistema operativo. El GIL afecta el rendimiento en sistemas multi-core, pero en algunas situaciones todavía puede obtener algún beneficio de múltiples núcleos (específicamente cuando el hilo pasa gran parte de su tiempo CPU encuadernada en una función de biblioteca C). – Duncan

+0

Podríamos discutir sobre eso. El hecho es que (c) Python está utilizando el mecanismo de enhebrado subyacente solo por conveniencia (¿Por qué reimplementar algo, eso ya está probado?), Pero te niega las ventajas de los hilos "reales". Entonces a) sí, técnicamente son un hilo de nivel os, pero b) NO no son un hilo de nivel os, porque no obtienes ninguno de los beneficios además de la memoria compartida de un hilo de nivel os. Es incluso así que recomendaría NO usarlos si tienes un paralelismo en mente para aumentar tu rendimiento. –

+0

Hubo una vez una conversación, que demostró que incluso puede ralentizarte considerablemente, si comienzas a muchos hilos (en algunas condiciones raras, incluso con funciones externas), debido a la capa adicional para el cambio de contexto. camina, nada y grazna como un pato, ¡incluso si no nació como un pato! ;-) –

10

No estoy familiarizado con la puesta en práctica, así que vamos a hacer un experimento:

import threading 
import time 

def target(): 
    while True: 
     print 'Thread working...' 
     time.sleep(5) 

NUM_THREADS = 5 

for i in range(NUM_THREADS): 
    thread = threading.Thread(target=target) 
    thread.start() 
  1. El número de procesos mencionados al usar ps -o cmd,nlwp <pid> es NUM_THREADS+1 (uno más para el hilo principal), de modo que mientras las herramientas del sistema operativo detecten el número de hilos, deberían ser hilos del sistema operativo. Intenté ambos con cpython y jython y, a pesar de que en jython hay algunos otros hilos en ejecución, por cada hilo adicional que agregue, ps incrementa el número de hilos en uno.

  2. No estoy seguro acerca del comportamiento htop, pero ps parece ser consistente.

  3. que añade la siguiente línea antes de iniciar las discusiones:

    thread.daemon = True 
    

    Cuando ejecuté el uso de CPython, el programa termina casi de inmediato y sin proceso fue encontrado usando ps, así que yo creo que el programa termina junto con los hilos. En jython, el programa funcionó de la misma manera (no terminó), así que tal vez haya otros subprocesos del jvm que impidan que el programa finalice o los subprocesos del daemon no sean compatibles.

Nota: he usado Ubuntu 11.10 con el pitón 2.7.2+ y jython 2.2.1 en la java1.6.0_23

0

Hay grandes respuestas a la pregunta, pero me siento la pregunta hilos demonio es todavía no explicado de una manera simple. Así que esta respuesta se refiere simplemente a la tercera pregunta

"hilo principal termina cuando se terminan todos los hilos no demonio."

Por lo tanto, los subprocesos de daemon python no son parte del programa Python si "todo el programa Python se cierra cuando solo quedan subprocesos de daemon"?

Si se piensa en lo que es un demonio, por lo general es un servicio. Algún código que se ejecuta en un bucle infinito, que sirve solicitudes, llena colas, acepta conexiones, etc. Otros hilos lo usan. No tiene ningún propósito cuando se ejecuta solo (en un solo proceso, términos).

Así que el programa no puede esperar a que finalice el hilo del daemon, porque puede que nunca suceda. Python finalizará el programa cuando hayan terminado todos los hilos no daemon. También detiene los hilos daemon .

Para esperar hasta que daemon thread haya completado su trabajo, utilice el método join(). daemon_thread.join() hará que Python espere también el hilo del daemon antes de salir. El join() también acepta un argumento de tiempo de espera.