web-dev-qa-db-fra.com

Process.waitFor (), threads et InputStreams

En pseudocode, voici ce que je fais:

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

Cependant, parfois processOutputStreamInThread ne voit aucune sortie et parfois il le fait. En gros, la méthode crée un BufferedInputStream de la sortie de la commande et l'envoie à un enregistreur.

Sur la base de ce que je vois, je suppose que command n'a pas besoin que toute sa sortie soit transférée dans les flux alimentés par getInputStream() et getErrorStream(), permettant ainsi à la flux pour être vide.

Les résultats de mes essais sont les questions suivantes:

(1) Est-ce que waitFor() dans Java.lang.Process nécessite que la sortie du programme exécuté ait été lire avant son retour?

La documentation indique seulement:

oblige le thread actuel à attendre, si nécessaire, jusqu'à ce que le processus représenté par cet objet Process soit terminé. Cette méthode renvoie immédiatement si le sous-processus est déjà terminé. Si le sous-processus n'est pas encore terminé, le thread appelant sera bloqué jusqu'à la fin du sous-processus.

(2) Dans quelles conditions les flux fournis par getInputStream et getErrorStream doivent être fermés et/ou sont ils ont fermé automatiquement?

La documentation indique seulement:

Obtient le flux d'erreurs du sous-processus. Le flux obtient des données canalisées à partir du flux de sortie d'erreur du processus représenté par cet objet Process.

Note d'implémentation: C'est une bonne idée que le flux d'entrée soit mis en mémoire tampon.

Un l'utilisateur rapporte qu'il a dû fermer lui-même les flux, mais j'obtiens une exception au moins une partie du temps indiquant que le flux est déjà fermé lorsque j'essaie de le faire.

Modifier: a changé getOutputStream en getInputStream, maintenant présent ci-dessus.

Résolution: Le problème a fini par être que dans certains cas, les threads utilisés pour traiter le flux de sortie ne s'exécutaient qu'après la fin de mon processus de très courte durée terminé, le flux d'entrée ne me donnant aucune donnée. waitFor n'a pas attendu la sortie du programme exécuté. Au lieu de cela, le programme s'est exécuté et s'est terminé avant qu'aucune sortie ne puisse être collectée.

J'ai utilisé des threads parce que je ne suis pas sûr de la quantité de sortie que j'allais obtenir sur l'erreur standard et la sortie standard et je voulais pouvoir traiter les deux simultanément, sans bloquer l'un ou l'autre si un seul d'entre eux avait des données disponibles. Mais, parce que mes threads ne peuvent pas toujours lire la sortie du programme exécuté, ce n'est pas une solution.

Mon code final ressemblait à ceci:

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

Si votre processus externe attend quelque chose sur son stdin, vous DEVEZ fermer le getOutputStream. Sinon, vous waitFor pour toujours.

Voici le quand Runtime.exec () ne fera pas d'article de JavaWorld qui décrit les différents pièges de la méthode exec et comment les éviter.

D'après mon expérience, il est préférable de consommer STDOUT et STDERR du processus enfant (jusqu'à ce qu'ils EOF), puis bloquer dans waitFor. Espérons qu'à ce stade, vous n'aurez pas à attendre longtemps .

Une réponse à la question de Kaleb. Dans des conditions normales, vous ne devez pas fermer les flux, mais parce que vous êtes waitingFor et pour une raison quelconque, il n'a pas de délai d'expiration, vous devrez peut-être fermer ces flux si vous rencontrez des conditions d'erreur dans la sortie et ne 't veulent traiter la sortie de l'enfant plus loin. Cependant, le fait que le programme enfant se termine (se bloque) lorsque son canal STDOUT ou STDERR est fermé à l'autre extrémité dépend entièrement de l'implémentation de cet enfant. Cependant, la plupart des programmes Shell prendront fin dans ces conditions.

J'aimerais vraiment que waitFor ait un certain délai d'attente significatif et que le Process ait une manière documentée de nettoyer ses ressources lorsque vous avez décidé d'abandonner sa surveillance.

29

Je pense que c'est un peu contre-intuitif, mais:

getOutputStream Obtient le flux de sortie du sous-processus. La sortie vers le flux est dirigée vers le flux d'entrée standard du processus représenté par cet objet Process. Note d'implémentation: C'est une bonne idée que le flux de sortie soit mis en mémoire tampon. Renvoie: le flux de sortie connecté à l'entrée normale du sous-processus.

J'ai lu cela en tant que flux de sortie du processus principal et attaché à l'entrée standard du sous-processus, donc lorsque vous écrivez dans getOutputStream (). Write () vous écrivez réellement sur le stdin.

Souhaitez-vous éventuellement utiliser .getInputStream ()?

Renvoie: le flux d'entrée connecté à la sortie normale du sous-processus.

Quant à Process.Waitfor (), les documents de l'API disent:

oblige le thread actuel à attendre, si nécessaire, jusqu'à ce que le processus représenté par cet objet Process soit terminé. Cette méthode renvoie immédiatement si le sous-processus est déjà terminé. Si le sous-processus n'est pas encore terminé, le thread appelant sera bloqué jusqu'à la fin du sous-processus.

Je dirais que le thread appelé est bloqué jusqu'à ce que le processus soit terminé - vous pouvez toujours traiter la sortie à ce stade en fonction des autres threads ou ils peuvent avoir terminé avant le retour de votre thread.

2
user257111