thread parallel lock async python subprocess multiprocessor hung

parallel - python multiprocessor



¿Detener la salida del proceso de lectura en Python sin bloqueo? (5)

Tengo un programa de Python para Linux casi se parece a este:

import os import time process = os.popen("top").readlines() time.sleep(1) os.popen("killall top") print process

el programa se cuelga en esta línea:

process = os.popen("top").readlines()

y eso sucede en las herramientas que mantienen la salida de la actualización como "Superior"

mis mejores pruebas:

import os import time import subprocess process = subprocess.Popen(''top'') time.sleep(2) os.popen("killall top") print process

funcionó mejor que el primero (es kelled), pero regresa:

<subprocess.Popen object at 0x97a50cc>

la segunda prueba:

import os import time import subprocess process = subprocess.Popen(''top'').readlines() time.sleep(2) os.popen("killall top") print process

lo mismo que el primero. Se colgó debido a "readlines ()"

Su regreso debería ser así:

top - 05:31:15 up 12:12, 5 users, load average: 0.25, 0.14, 0.11 Tasks: 174 total, 2 running, 172 sleeping, 0 stopped, 0 zombie Cpu(s): 9.3%us, 3.8%sy, 0.1%ni, 85.9%id, 0.9%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 1992828k total, 1849456k used, 143372k free, 233048k buffers Swap: 4602876k total, 0k used, 4602876k free, 1122780k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 31735 Barakat 20 0 246m 52m 20m S 19.4 2.7 13:54.91 totem 1907 root 20 0 91264 45m 15m S 1.9 2.3 38:54.14 Xorg 2138 Barakat 20 0 17356 5368 4284 S 1.9 0.3 3:00.15 at-spi-registry 2164 Barakat 9 -11 164m 7372 6252 S 1.9 0.4 2:54.58 pulseaudio 2394 Barakat 20 0 27212 9792 8256 S 1.9 0.5 6:01.48 multiload-apple 6498 Barakat 20 0 56364 30m 18m S 1.9 1.6 0:03.38 pyshell 1 root 20 0 2880 1416 1208 S 0.0 0.1 0:02.02 init 2 root 20 0 0 0 0 S 0.0 0.0 0:00.02 kthreadd 3 root RT 0 0 0 0 S 0.0 0.0 0:00.12 migration/0 4 root 20 0 0 0 0 S 0.0 0.0 0:02.07 ksoftirqd/0 5 root RT 0 0 0 0 S 0.0 0.0 0:00.00 watchdog/0 9 root 20 0 0 0 0 S 0.0 0.0 0:01.43 events/0 11 root 20 0 0 0 0 S 0.0 0.0 0:00.00 cpuset 12 root 20 0 0 0 0 S 0.0 0.0 0:00.02 khelper 13 root 20 0 0 0 0 S 0.0 0.0 0:00.00 netns 14 root 20 0 0 0 0 S 0.0 0.0 0:00.00 async/mgr 15 root 20 0 0 0 0 S 0.0 0.0 0:00.00 pm

y guardar en la variable "proceso". Alguna idea chicos, estoy realmente atrapado ahora?


En lugar de usar "arriba", sugiero usar "ps" que le dará la misma información, pero solo una vez en lugar de una vez por segundo para toda la eternidad.

También necesitarás usar algunas banderas con ps, tiendo a usar "ps aux"


Lo que haría, en lugar de este enfoque, es examinar el programa del que está tratando de obtener información y determinar la fuente final de esa información. Puede ser una llamada API o nodo de dispositivo. Luego, escribe un pitón que lo obtenga de la misma fuente. Eso elimina los problemas y la sobrecarga de "rozar" datos "cocinados".


(JF Sebastian tus códigos funcionan bien, creo que es mejor que mi solución =))

Lo he solucionado usando otra forma.

En lugar de hacer la salida directamente en el terminal, lo hago en un archivo "tmp_file":

top >> tmp_file

luego usé la herramienta "cortar" para hacer que su salida "que es la salida superior" como valor del proceso

cat tmp_file

e hizo lo que yo quiero que haga. Este es el código final:

import os import subprocess import time subprocess.Popen("top >> tmp_file",shell = True) time.sleep(1) os.popen("killall top") process = os.popen("cat tmp_file").read() os.popen("rm tmp_file") print process # Thing better than nothing =)

Muchas gracias muchachos por ayuda


En realidad, si llenas el buffer de salida, terminarás con alguna respuesta. Entonces, una solución es llenar el buffer con una gran salida de basura (~ 6000 caracteres con bufsize = 1).

Digamos que, en lugar de arriba, tienes un script de python que escribe en sys.stdout:

GARBAGE=''./n'' sys.stdout.write(valuable_output) sys.stdout.write(GARBAGE*3000)

En el lado del iniciador, en lugar de simple process.readline ():

GARBAGE=''./n'' line=process.readline() while line==GARBAGE: line=process.readline()

Muy cierto que está un poco sucio ya que 2000 depende de la implementación del subproceso, pero funciona bien y es muy simple. configurar cualquier cosa menos bufsize = 1 empeora la situación.


#!/usr/bin/env python """Start process; wait 2 seconds; kill the process; print all process output.""" import subprocess import tempfile import time def main(): # open temporary file (it automatically deleted when it is closed) # `Popen` requires `f.fileno()` so `SpooledTemporaryFile` adds nothing here f = tempfile.TemporaryFile() # start process, redirect stdout p = subprocess.Popen(["top"], stdout=f) # wait 2 seconds time.sleep(2) # kill process #NOTE: if it doesn''t kill the process then `p.wait()` blocks forever p.terminate() p.wait() # wait for the process to terminate otherwise the output is garbled # print saved output f.seek(0) # rewind to the beginning of the file print f.read(), f.close() if __name__=="__main__": main()

Soluciones tipo cola que imprimen solo la parte de la salida

Puede leer el resultado del proceso en otro hilo y guardar el número requerido de las últimas líneas en una cola:

import collections import subprocess import time import threading def read_output(process, append): for line in iter(process.stdout.readline, ""): append(line) def main(): # start process, redirect stdout process = subprocess.Popen(["top"], stdout=subprocess.PIPE, close_fds=True) try: # save last `number_of_lines` lines of the process output number_of_lines = 200 q = collections.deque(maxlen=number_of_lines) # atomic .append() t = threading.Thread(target=read_output, args=(process, q.append)) t.daemon = True t.start() # time.sleep(2) finally: process.terminate() #NOTE: it doesn''t ensure the process termination # print saved lines print ''''.join(q) if __name__=="__main__": main()

Esta variante requiere que q.append() sea ​​una operación atómica. De lo contrario, la salida podría estar dañada.

solución signal.alarm()

Puede usar signal.alarm() para llamar al process.terminate() después del tiempo de espera especificado en lugar de leer en otro hilo. Aunque podría no interactuar muy bien con el módulo de subprocess . Basado en la respuesta de @Alex Martelli :

import collections import signal import subprocess class Alarm(Exception): pass def alarm_handler(signum, frame): raise Alarm def main(): # start process, redirect stdout process = subprocess.Popen(["top"], stdout=subprocess.PIPE, close_fds=True) # set signal handler signal.signal(signal.SIGALRM, alarm_handler) signal.alarm(2) # produce SIGALRM in 2 seconds try: # save last `number_of_lines` lines of the process output number_of_lines = 200 q = collections.deque(maxlen=number_of_lines) for line in iter(process.stdout.readline, ""): q.append(line) signal.alarm(0) # cancel alarm except Alarm: process.terminate() finally: # print saved lines print ''''.join(q) if __name__=="__main__": main()

Este enfoque funciona solo en los sistemas * nix. Podría bloquear si process.stdout.readline() no regresa.

threading.Timer solución

import collections import subprocess import threading def main(): # start process, redirect stdout process = subprocess.Popen(["top"], stdout=subprocess.PIPE, close_fds=True) # terminate process in timeout seconds timeout = 2 # seconds timer = threading.Timer(timeout, process.terminate) timer.start() # save last `number_of_lines` lines of the process output number_of_lines = 200 q = collections.deque(process.stdout, maxlen=number_of_lines) timer.cancel() # print saved lines print ''''.join(q), if __name__=="__main__": main()

Este enfoque también debería funcionar en Windows. Aquí he usado process.stdout como iterable; podría introducir un búfer de salida adicional, podría cambiar al enfoque iter(process.stdout.readline, "") si no es deseable. si el proceso no finaliza en process.terminate() , los scripts se bloquean.

Sin hilos, sin solución de señales

import collections import subprocess import sys import time def main(): args = sys.argv[1:] if not args: args = [''top''] # start process, redirect stdout process = subprocess.Popen(args, stdout=subprocess.PIPE, close_fds=True) # save last `number_of_lines` lines of the process output number_of_lines = 200 q = collections.deque(maxlen=number_of_lines) timeout = 2 # seconds now = start = time.time() while (now - start) < timeout: line = process.stdout.readline() if not line: break q.append(line) now = time.time() else: # on timeout process.terminate() # print saved lines print ''''.join(q), if __name__=="__main__": main()

Esta variante no utiliza hilos ni señales, pero produce una salida distorsionada en el terminal. Bloqueará si los bloques process.stdout.readline() .