2010-01-27 11 views
31

In pseudocodice, ecco quello che sto facendo:Process.waitFor(), filati, e InputStreams

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

Tuttavia, a volte processOutputStreamInThread non vede alcun output e, a volte lo fa. Approssimativamente, il metodo crea un BufferedInputStream dell'output del comando e lo invia a un logger.

In base a quello che vedo, sto cercando di indovinare che command non deve avere tutto il suo output scaricati in corsi d'acqua alimentati da getInputStream() e getErrorStream(), permettendo in tal modo il flusso di essere vuota.

I risultati delle mie prove sono le seguenti domande:

(1) Vuol waitFor() nel java.lang.Process richiedono l'output del programma eseguito a sono stati letti prima che ritorni?

La documentazione indica solo:

provoca il thread corrente di aspettare, se necessario, fino a quando il processo rappresentata dall'oggetto Process è terminato. Questo metodo restituisce immediatamente se il sottoprocesso è già terminato. Se il sottoprocesso non è ancora terminato, il thread chiamante verrà bloccato fino alla chiusura del sottoprocesso.

(2) A quali condizioni fanno i flussi forniti da getInputStream e getErrorStream devono essere chiusi e/o sono chiuse automaticamente?

La documentazione afferma soltanto:

ottiene il flusso di errore del sottoprocesso. Lo stream ottiene i dati inviati tramite pipe dal flusso di output di errore del processo rappresentato da questo oggetto Process.

Nota di implementazione: È consigliabile memorizzare il buffer di input.

Uno user reports che ha dovuto chiudere il se stesso flussi, ma ottengo un'eccezione, almeno una parte del tempo che indica che il flusso è già chiusa quando tento di farlo.

Modifica: modificato getOutputStream a getInputStream, ora presente sopra.

Risoluzione: Il problema ha finito per essere che in alcuni casi i fili utilizzati per elaborare il flusso di output non sarebbe correre fino a dopo il mio processo molto breve aveva completato, con conseguente flusso di input avermi dato nessun dato. waitFor non ha atteso l'output del programma eseguito. Piuttosto, il programma è stato eseguito e terminato prima che qualsiasi output potesse essere raccolto.

Ho usato thread perché non sono sicuro di quanto output avrei dovuto ottenere su errore standard e output standard e volevo essere in grado di elaborare entrambi contemporaneamente, senza bloccare uno o l'altro se solo uno di essi avesse dati disponibili. Ma, poiché i miei thread non possono leggere in modo coerente l'output del programma eseguito, è una non soluzione.

miei Coded finali sembravano qualcosa di simile:

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

risposta

25

Se il processo esterno si aspetta qualcosa sul suo stdin, DEVI chiudere lo getOutputStream. Altrimenti ti arriverà per sempre waitFor.

Questo è il When Runtime.exec() won't article di JavaWorld che descrive le diverse insidie ​​del metodo exec e come evitarli.

Dalla mia esperienza è meglio consumare STDOUT e STDERR del processo figlio (fino a EOF) e quindi bloccare in waitFor. Spero che a questo punto non dovrai aspettare per molto.

Una risposta alla domanda di Kaleb. In condizioni normali non si dovrebbero chiudere i flussi, tuttavia poiché si è waitingFor e per qualsiasi motivo non ha un timeout, potrebbe essere necessario chiudere questi flussi se si incontrano alcune condizioni di errore nell'output e non si desidera elaborare ulteriormente l'output del bambino. Tuttavia, se il programma figlio verrà terminato (arresto anomalo) quando la pipe STDOUT o STDERR è chiusa all'altra estremità è totalmente all'altezza dell'implementazione di quel bambino.La maggior parte dei programmi di shell, tuttavia, terminerà in tali condizioni.

Mi piacerebbe davvero che ilavesse un timeout significativo e il Process aveva un modo documentato per ripulire le sue risorse quando hai deciso di abbandonare il suo monitoraggio.

+0

Anche gli altri flussi devono essere chiusi se vengono utilizzati? Sono implicitamente chiusi quando il processo termina? –

+0

@Kaleb Ho aggiunto una risposta alla tua domanda al mio post. –

+4

Per quanto riguarda l'articolo JavaWorld, c'è un bug con la soluzione finale. Se un processo si interrompe molto rapidamente, errorGobbler e outputGobbler potrebbero non aver ancora eseguito e consumato i dati. Il codice dovrebbe essere: proc.waitFor(); errorGobbler.join(); outputGobbler.join() ;. Ciò ha costretto il thread principale ad attendere fino a quando i flussi non hanno finito di leggere l'input. –

2

Penso che questo sia un po 'contro-intuitivo, ma:

getOutputStream Ottiene il flusso di output del sottoprocesso. Uscita a il flusso viene reindirizzato allo stream di input standard del processo rappresentato da questo oggetto Processo. Nota di implementazione: È una buona idea per il buffer di output da bufferizzare. Restituisce: il flusso di uscita collegato all'ingresso normale del sottoprocesso.

ho letto che in questo flusso in uscita dal processo principale ed è attaccato allo standard input del sub-processo, in modo che quando si scrive a getOutputStream(). Write() in realtà si sta scrivendo sul stdin.

Si consiglia di utilizzare .getInputStream()?

Resi: il flusso di input collegato all'uscita normale del sottoprocesso.

Quanto Process.waitFor() la documentazione API dire:

causa il thread corrente di attesa, se necessario, fino a quando il processo rappresentata dall'oggetto Process ha terminato. Questo metodo restituisce immediatamente se il sottoprocesso ha già terminato. Se il sottoprocesso non è stato ancora terminato, il thread di chiamata verrà bloccato fino alla chiusura del sottoprocesso .

Direi che il thread in cui viene chiamato verrà bloccato fino a quando il processo non sarà terminato - si può ancora elaborare l'output in questa fase a seconda degli altri thread o potrebbero essere terminati prima del ritorni del filo.

+0

Il 'getOutputStream()' che avevo nella mia domanda era un refuso. Era davvero 'getInputStream()' nel mio codice reale e non sarebbe stato compilato perché la funzione di elaborazione si aspettava un 'InputStream'. –

+0

Penso che tu abbia esattamente ragione sui miei thread che stanno ancora elaborando l'output. Il più delle volte, quando ho eseguito la modalità di debug, tutto è andato a buon fine poiché tutto è andato molto più lentamente. Quando si esegue senza il debugger, di solito fallirebbe. Pubblicherò la mia soluzione alternativa più tardi. –

Problemi correlati