2010-01-27 15 views
31

En pseudocódigo, esto es lo que estoy haciendo:Process.waitFor(), hilos y InputStreams

Process proc = runtime.exec(command); 
processOutputStreamInThread(proc.getInputStream()); 
processOutputStreamInThread(proc.getErrorStream()); 
proc.waitFor() 

Sin embargo, a veces processOutputStreamInThread no se ve ninguna salida y, a veces lo hace. Aproximadamente, el método crea un BufferedInputStream de la salida del comando y lo envía a un registrador.

Sobre la base de lo que estoy viendo, supongo que command no necesita tener todo esto es la salida vierten en los arroyos alimentados por getInputStream() y getErrorStream(), permitiendo así que la corriente esté vacío.

Los resultados de mis pruebas son las siguientes preguntas:

(1) ¿Se waitFor() en java.lang.Process requieren la salida del programa ejecutado ha sido leído antes de que vuelva?

La documentación sólo se afirma:

hace que el hilo actual que esperar, si es necesario, hasta que el proceso representado por este objeto Process ha terminado. Este método regresa inmediatamente si el subproceso ya ha finalizado. Si el subproceso aún no ha terminado, la cadena de llamada será bloqueada hasta que el subproceso finalice.

(2) ¿En qué condiciones las corrientes proporcionadas por getInputStream y getErrorStream deben ser cerradas y/o son cerrados de forma automática?

La documentación sólo se afirma:

Obtiene el flujo de error del subproceso. La secuencia obtiene datos transmitidos desde la secuencia de salida de error del proceso representado por este objeto de proceso.

Nota de implementación: es una buena idea que el flujo de entrada se almacene en el búfer.

Una user reports que tuvo que cerrar el mismo arroyos, pero me da una excepción al menos una parte del tiempo que indica que la corriente ya está cerrado cuando intento hacerlo.

Editar: cambió getOutputStream-getInputStream, ahora presente anteriormente.

Resolución: El problema terminó siendo que, en ciertos casos, los hilos utilizados para procesar la secuencia de salida no se ejecutarían hasta que se completara mi proceso de corta duración, lo que daría como resultado que la corriente de entrada no me proporcionara datos. waitFor no esperó la salida del programa ejecutado. Más bien, el programa se ejecutó y finalizó antes de que se pudiera recopilar cualquier resultado.

que utilizan hilos porque no estoy seguro de cuánto de salida que iba a conseguir en el error estándar y la salida estándar y que quería ser capaz de procesar ambos a la vez, sin bloquear uno o el otro debe sólo uno de ellos tiene datos disponibles. Pero, como mis hilos no pueden leer de forma consistente la salida del programa ejecutado, no es una solución.

Mis Coded finales parecían algo como esto:

ProcessBuilder pb = new ProcessBuilder(cmdargs); 
pb.redirectErrorStream(true); 
Process proc = pb.start(); 
processOutputStream(proc.getInputStream()); 
proc.waitFor() 

Respuesta

25

Si su proceso externo espera algo en su stdin, DEBE cerrar el getOutputStream. De lo contrario, va a waitFor para siempre.

Aquí está el When Runtime.exec() won't article de JavaWorld que describe las diferentes trampas del método exec y cómo evitarlas.

Desde mi experiencia, es mejor consumir STDOUT y STDERR del proceso secundario (hasta EOF) y luego bloquear en waitFor. Esperemos que en este momento no tenga que esperar por mucho tiempo.

Una respuesta a la pregunta de Kaleb. En condiciones normales, no debe cerrar las transmisiones, sin embargo, como es waitingFor y por alguna razón no tiene un tiempo de espera, es posible que deba cerrar estas secuencias si se producen algunas condiciones de error en la salida y no desea procesar la producción del niño más. Sin embargo, si el programa secundario terminará (bloqueará) cuando esté cerrado STDOUT o STDERR se cerrará en el otro extremo, estará totalmente a la altura de la implementación de ese niño.Sin embargo, la mayoría de los programas de shell terminarán bajo tales condiciones.

Realmente deseo waitFor tuvo un tiempo de espera significativo y el Process tenía una forma documentada de limpiar sus recursos cuando decidió abandonar su supervisión.

+0

¿Deben cerrarse también las otras corrientes si se usan? ¿Están implícitamente cerrados cuando el proceso termina? –

+0

@Kaleb He agregado una respuesta a tu pregunta a mi publicación. –

+4

En cuanto al artículo de JavaWorld, hay un error con la solución final. Si un proceso finaliza muy rápidamente, es posible que errorGobbler y outputGobbler aún no hayan ejecutado y consumido datos. El código debe ser: proc.waitFor(); errorGobbler.join(); outputGobbler.join() ;. Esto obligó al hilo principal a esperar hasta que las secuencias terminen de leer la entrada. –

2

creo que esto es algo contrario a la intuición, pero:

getOutputStream Obtiene el flujo de salida del subproceso. Salida a la transmisión se canaliza a la corriente de entrada estándar del proceso representado por este objeto de proceso. Nota de implementación: es una buena idea para el flujo de salida que se almacenará en el búfer. Devuelve: la secuencia de salida conectada a la entrada normal del subproceso.

leí que a medida que este flujo de salida del proceso principal y está unida a la entrada estándar de la sub-proceso, por lo que cuando se escribe a getOutputStream(). Write() en realidad se está escribiendo en la entrada estándar.

¿Es posible que desee utilizar .getInputStream()?

Devuelve: el flujo de entrada conectado a la salida normal del subproceso.

En cuanto a Process.waitFor() los documentos de la API dicen:

hace que el hilo actual a esperar, si necesario, hasta que el proceso representado por este objeto de proceso tiene terminado. Este método devuelve inmediatamente si el subproceso tiene ya finalizado. Si el subproceso aún no ha finalizado, la secuencia de llamada se bloqueará hasta que finalice el subproceso .

Yo diría que el hilo de lo que se llama en será bloqueado hasta el momento en que el proceso ha terminado de ejecutarse - usted todavía puede ser procesado de salida en esta etapa en función de los otros hilos o pueden haber terminado antes de que su hilo vuelve.

+0

El 'getOutputStream()' que tenía en mi pregunta era un error tipográfico. Realmente era 'getInputStream()' en mi código real y no se habría compilado porque la función de procesamiento espera un 'InputStream'. –

+0

Creo que tienes razón cuando mis hilos continúan procesando el resultado. La mayoría de las veces, cuando ejecutaba el modo de depuración, todo se ejecutaba con éxito, ya que todo funcionaba mucho más lento. Cuando se ejecuta sin el depurador, generalmente falla. Publicaré mi solución más adelante. –