Foros Python
matar procesos y subprocesos - Versión para impresión

+- Foros Python (https://foro.recursospython.com)
+-- Foro: Desarrollo (https://foro.recursospython.com/forumdisplay.php?fid=1)
+--- Foro: General (https://foro.recursospython.com/forumdisplay.php?fid=9)
+--- Tema: matar procesos y subprocesos (/showthread.php?tid=139)



matar procesos y subprocesos - calvicius - 18-03-2018

hola,
tengo un programa que dentro de un hilo corre un subprocess.Popen. Este proceso recibe unos datos iniciales y empieza a soltar  datos procesados en función de esos datos iniciales, indefinidamente.
El problema es cuando quiero enviarle nuevos datos inciales mientras está procesando las salidas anteriores. Me sigue soltando los datos viejos que tiene acumulados.

El programa simplificado es este:


Código:
# Python 3.6.1
import subprocess, time, threading

def inicia_robot(posicion):
   posic_uci = 'position fen ' + posicion
   
   engine = subprocess.Popen(
       'stockfish.exe',
       bufsize=0,
       universal_newlines=True,
       stdin=subprocess.PIPE,
       stdout=subprocess.PIPE,
   )
   
   print ("id. proceso motor: ", engine.pid)

   def put(comando):
       print('\nTu:\n\t'+comando)
       engine.stdin.write(comando+'\n')

   def get():
       # using the 'isready' command (engine has to answer 'readyok')
       # to indicate current last line of stdout
       engine.stdin.write('isready\n')
       print('\nmotor:')
       while True:
           text = engine.stdout.readline().strip()
           if text == 'readyok':
               break
           if text !='':
               print('\t'+text)

   get()
   put('uci')
   get()
   put('setoption name Hash value 128')
   get()
   #put('setoption name nodestime value 500')
   #get()
   #put('setoption name Threads value 4')
   #get()
   put('ucinewgame')
   get()
   put(posic_uci)
   get()
   put('go infinite')
   time.sleep(2)
   get()
   #put('stop')
   #get()
   #put('quit')
   
   
if __name__ == '__main__':
   fen = 'r4rk1/pp2bppp/2n2n2/q3p1B1/2B3bN/1P2P3/P2N1PPP/2RQ1RK1 w - - 0 15'
   t = threading.Thread(target=inicia_robot, args=(fen, ))
   t.start()
   #t.join()

He intentado matar el proceso (terminandose automaticamente el hilo) y sus hijos asi:


Código:
try:
           if os.name != 'nt'  # POSIX
               os.kill(proceso_motor.pid, 9)
               
           else:   # WINDOWS
               #import ctypes
               #PROCESS_TERMINATE = 1
               #handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, pid)
               #ctypes.windll.kernel32.TerminateProcess(handle, -1)
               #ctypes.windll.kernel32.CloseHandle(handle)
               # por si acaso no mata los hijos
               #st = subprocess.STARTUPINFO()
               #st.dwFlags |= subprocess.STARTF_USESHOWWINDOW
               DETACHED_PROCESS = 0x00000008   # para evitar que salga nada por la consola
               subprocess.call(['taskkill', '/F', '/T', '/PID', str(proceso_motor.pid)], \
                           creationflags=DETACHED_PROCESS)  #, startupinfo=st)
       except OSError:
           pass
       finally:
            if not proceso_motor.pid:
               motor_activo = False

pero no me funciona bien. La mayor parte de las veces deja algun "hijo" colgando.

Una alternativa podría ser poder mandar una señal o evento externo al hilo con un comando tipo STOP para que pare el hilo (put('stop')), pero desde fuera del hilo. Aqui ando un poco perdido.

¿alguna idea?


RE: matar procesos y subprocesos - calvicius - 19-03-2018

Me ha costado, pero al final funciona:

Dentro de la clase :

Código:
def mata_proceso(self):
       print("=======matando subproceso self.engine en WINDOWS ==============")
        # cerramos la pantalla como si fuesen ficheros al estilo unix
       self.engine.stdin.close()
       self.engine.stdout.close()
       self.engine.wait() # bloquea y espera a que terminen el proceso y sus posibles hijos
       
       if self.engine.poll() is None:  # si el proceso no ha terminado
           try:
               #self.engine.terminate() # lo más simple ya que voy a cerrar stdin y stdout
               # especifico de windows. De este modo no sale nada en pantalla:
               st = subprocess.STARTUPINFO()
               st.dwFlags |= subprocess.STARTF_USESHOWWINDOW
               DETACHED_PROCESS = 0x00000008   # para evitar que salga nada por la consola
               subprocess.call(['taskkill', '/F', '/T', '/PID', str(self.engine.pid)], \
                           creationflags=DETACHED_PROCESS, startupinfo=st)
               #subprocess.call('taskkill /F /T /PID ' + str(self.engine.pid))
           except:
               print("excepcion en poll = None")
       
       # esto es para comprobación en pantalla: da error --> el pid no se encuentra
       try:
           subprocess.call('taskkill /F /T /PID ' + str(self.engine.pid))
       except:
           print("No se ha encontrado el proceso ", self.engine.pid)
       
       #el metodo pid de self.engine sigue existiendo
       print(70, self.engine.pid)    # imprime el num de proceso, pero realmente no existe

en el __main__ :

Código:
fen = 'r4rk1/pp2bppp/2n2n2/q3p1B1/2B3bN/1P2P3/P2N1PPP/2RQ1RK1 w - - 0 15'
   xxx = IniciaRobot(fen)
   
   t = threading.Thread(target=xxx.inicia_proceso)
   t.start()
   
   time.sleep(15)
   xxx.mata_proceso()
   
   t.join()