2009-05-25 7 views
59

Esto podría ser una pregunta estúpida, pero estoy probando algunas de mis suposiciones sobre Python y estoy confundido en cuanto a por qué el siguiente fragmento de código no saldría cuando se llama en el hilo, pero saldría cuando se llame en el principal hilo.¿Por qué sys.exit() no sale cuando se llama dentro de un hilo en Python?

import sys, time 
from threading import Thread 

def testexit(): 
    time.sleep(5) 
    sys.exit() 
    print "post thread exit" 

t = Thread(target = testexit) 
t.start() 
t.join() 
print "pre main exit, post thread exit" 
sys.exit() 
print "post main exit" 

Los documentos para sys.exit() indican que la llamada debe salir de Python. Puedo ver en la salida de este programa que "publicar salida de hilo" nunca se imprime, pero el hilo principal simplemente continúa incluso después de que sale el hilo.

¿Hay una instancia separada del intérprete que se está creando para cada subproceso, y la llamada a exit() solo está saliendo de esa instancia separada? De ser así, ¿cómo gestiona la implementación de subprocesos el acceso a los recursos compartidos? ¿Qué sucede si quiero salir del programa del hilo (no es lo que realmente quiero, pero entiendo)?

Respuesta

47

sys.exit() provoca la excepción SystemExit, al igual que thread.exit(). Entonces, cuando sys.exit() levanta esa excepción dentro de ese hilo, tiene el mismo efecto que llamar a thread.exit(), por lo que solo sale el hilo.

8

¿El hecho de que "salida principal pre, salida de hilo posterior" está impreso lo que le molesta?

A diferencia de otros idiomas (como Java), donde el análogo a sys.exit (System.exit, en el caso de Java) hace que el VM/proceso/intérprete para detener de inmediato, Python sys.exit solo se produce una excepción: una excepción SystemExit en particular.

Éstos son los documentos para sys.exit (solo): print sys.exit.__doc__

la salida del intérprete elevando SystemExit (estado).
Si se omite el estado o Ninguno, se establece de manera predeterminada en cero (es decir, éxito).
Si el estado es numérico, se usará como el estado de salida del sistema.
Si es otro tipo de objeto, se imprimirá y el estado de salida
del sistema será uno (es decir, error).

Esto tiene algunas consecuencias:

  • en un hilo que sólo mata al hilo actual, no todo el proceso (asumiendo que recibe todo el camino hasta la cima de la pila ...)
  • destructores de objetos (__del__) están potencialmente invocadas como los marcos de pila que hacen referencia a esos objetos se desenrollan
  • finalmente se hace son ejecutados como la pila se desenrolla
  • que pueda coger un SystemExit excepción

La última es posiblemente el más sorprendente, y es otra razón por la que casi nunca debe tener una except frase fuera de contexto en el código Python.

12

¿Qué pasa si quería salir del programa de la rosca (no es que en realidad quiero, pero sólo por lo que entiendo)?

Mi método preferido es el envío de mensajes Erlang-ish. Simlified ligeramente, lo hago de esta manera:

import sys, time 
import threading 
import Queue # thread-safe 

class CleanExit: 
    pass 

ipq = Queue.Queue() 

def testexit(ipq): 
    time.sleep(5) 
    ipq.put(CleanExit) 
    return 

threading.Thread(target=testexit, args=(ipq,)).start() 
while True: 
    print "Working..." 
    time.sleep(1) 
    try: 
    if ipq.get_nowait() == CleanExit: 
     sys.exit() 
    except Queue.Empty: 
    pass 
+2

No necesita 'Queue' aquí. Solo un simple 'bool' saldrá bien. El nombre clásico para esta variable es 'is_active' y su valor predeterminado inicial es' True'. –

+3

Sí, estás en lo correcto. Según http://effbot.org/zone/thread-synchronization.htm, modificar un 'bool' (o cualquier otra operación atómica) funcionará perfectamente para este problema en particular. La razón por la que voy con 'Queue's es que cuando trabajo con agentes con hilos, suelo terminar necesitando varias señales diferentes (' flush', 'reconnect',' exit', etc ...) casi de inmediato. – Deestan

9

¿Qué pasa si quería salir del programa del hilo?

Además del método descrito Deestan puede llamar os._exit (nótese el guión bajo). Antes de usarlo, asegúrese de que comprende no limpiezas (como llamar al __del__ o similar).

+0

¿Vaciará la E/S? –

11

¿Qué sucede si quiero salir del programa desde el hilo (no es que yo realmente quiero, pero entiendo)?

Al menos en Linux que podría hacer algo como:

os.kill(os.getpid(), signal.SIGINT) 

Esta enviar un SIGINT al hilo principal, donde se puede manejar como un KeyboardInterrupt.

BTW: en Windows, solo puede enviar una señal SIGTERM, que no puede capturarse desde Python. (allí preferiría llamar a os._exit como lo sugiere Helmut)

Cuestiones relacionadas