Thread.sleep(100) und process.waitFor() blockieren Prozess/ProzessBuilder

T

Toni86

Hallo,

ich habe folgendes Problem: ich starte aus einem Java-Programm (P1) ein anderes Java-Programm (P2, Client). Dieses kommuniziert mit (P3, Server, unwichtig) und erzeugt nach einer unbestimmten aber endlichen Zeit eine Datei und wird beendet. Nachdem ich von P1 aus P2 gestartet habe, möchte ich als nächstes die von P2 erzeugte Datei lesen, ich muss also darauf warten.

P2 starte ich mittels
Code:
process = Runtime.getRuntime().exec(commandRunClient);
oder über einen ProcessBuilder; macht hier bez. meines Problems aber keinen Unterschied. Falls ich es über Runtime mache, kann ich zum Warten
Code:
process.waitFor();
verwenden. Wenn ich es über ProcessBuilder mache, habe ich eine Schleife, die immer per
Code:
Thread.sleep(1000);
wartet und dann prüft, ob die Datei erzeugt wurde.
In beiden Fällen stelle ich Fest, dass P2 unterbrochen oder blockiert wird, da P3 (der Server) "Client timeout" meldet. Wenn ich nicht auf die Datei warte, und P1 ohne die erwartete Datei beendet wird, läuft P2 ohne "Client timeout" weiter. Es liegt also definitiv daran, dass waitFor() und Thread.sleep() den Prozess blockieren. Meine Frage: Kann man das verhindern? Wenn ja, wie?

Hier ein Code-Auszug für den Fall mit ProcessBuilder:
Code:
    ProcessBuilder pb;
    Process processController;
    try {
        String commandRunClient = "command to start... specified somewhere else";
        pb = new ProcessBuilder("cmd", "/c", commandRunClient);
        processController = pb.start();
    } catch (IOException e) {
        System.out.println("evaluate(): error while trying to run the Controller");
        e.printStackTrace();
    }
    
    // Waiting for the file with the results or for timeout
    File resultsfile = new File(resultsFilename);
    boolean timeout = false;
    int seconds = 0;
    while ((!resultsfile.exists()) && (!timeout)) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("evaluate(): interrupted while waiting for the results");
            e.printStackTrace();
        }
        seconds++;
        if (seconds > 90) {
            timeout = true;
            processController.destroy();
        }
    }

Viele Grüße,
Toni
 
Also erstens : wenn du mit ProcessBuilder arbeitest erhälts du genau so ein Process wie über die Runtime-Methode da du dem ProcessBuilder ja auch sagen musst das er starten soll. Und ProcessBuilder.start() liefert ein Process zurück ... und auf diesen kannst du genau so waitFor() anwenden. Also nächste mal bitte einen Blick in die Doc werfen.

Das Problem selber sollte damit allerdings nicht so im Zusammenhang stehen wie du es beschreibst da der ChildProcess ein eigenständiger Process ist und somit vom ParentProcess so einfach nicht manipuliert werden kann. Das Problem wird denke ich der File-Caller sein. Damit hängst du nämlich einen exklusive-Lock auf das File was natürlich den ChildProcess blockiert. Versuche mal deinen Code ab Zeile 12 einfach nur das Process.waitFor() und erst DANACH das File-Objekt zu erzeugen. Das sollte das Lock-Problem lösen. Das ganze ist nämlich ein sog. DEADLOCK : der ParentProcess wartet bis der ChildProcess eine Datei erstellt hat die der ChildProcess aber nicht erstellen kann da er auf die Freigabe des File-Locks des ParentProcess wartet. Ist eigentlich ein sehr einfaches Problem wenn man weis was da genau passiert.
 
Und ProcessBuilder.start() liefert ein Process zurück ... und auf diesen kannst du genau so waitFor() anwenden. Also nächste mal bitte einen Blick in die Doc werfen.

Da habe ich mich missverständlich ausgedrückt: natürlich kann ich darauf auch waitFor() anwenden, allerdings ist das IMMER ein endloses warten, wenn der Prozess über ProcessBuilder.start() aufgerufen wurde, siehe Doc ;). Es bringt nichts, deshalb setze ich es mit nicht anwendbar gleich (ist einfach nicht praktikabel).

DEADLOCK : der ParentProcess wartet bis der ChildProcess eine Datei erstellt hat die der ChildProcess aber nicht erstellen kann da er auf die Freigabe des File-Locks des ParentProcess wartet.

Danke für den Hinweis. Das könnte später noch zu Problemen führen. Allerdings kommt es momentan noch nicht dazu, denn der Child-Process erzeugt die Datei erst ganz am Ende seiner Ausführung (Ergebnisdatei). Der Timeout bzw. die Blockierung findet aber während seiner "Hauptausführungszeit" statt, also bevor eine Methode aufgerufen wird, die eine Datei erzeugt. Ich kann ausschließen, dass es MOMENTAN am File Lock liegt.
 
Gut,
erklär mir bitte erstmal warum es ein IMMER ENDLOS ist wenn du über ProcessBuilder.start().waitFor() auf den ChildProcess wartest ... das wird mir aus der Doc nicht ersichtlich.
Zweitens : Poste doch einfach mal den komplette code alle 3 beteiligten Programme so das wir uns ein lauffähiges Sample zusammencompilen können ums zu debuggen. Weil das bisschen Source da oben hilft so überhaupt nicht da daraus nicht ersichtlich wird das der ChildProcess nicht am File-Lock hängen bleibt. Eher im gegenteil wird diese Vermutung durch den Source nur noch bestärkt.
Es wäre mit sicherheit auch interessant was der ChildProcess alles macht und was da übers Netz geht und wie der Server reagiert. Vielleicht liegt der Fehler nicht in dem oben gepostetem Launcher sondern ganz wo anders ... aber das können wir nur vermuten ohne ein vollständig compilebares Beispiel bei dem der beschriebene Fehler auftritt. Versuch doch mal in deinem "Clienten" die ganze Netz-Kommunikation rauszunehemen und nur das File zu erstellen *meinet wegen mit Random-Daten*. Wenn hierbei der Fehler nämlich nicht auftritt liegt der Wurm im Netz-Teil den du dann auch mal posten müsstest ... und zwar beide Seiten ... nicht nur eine.
Aber so kann dir glaube ich nur jemand helfen desse Glaskugel weder kaputt noch im Urlaub ist, was bei so ziemlich jedem hier zutrifft. *Es gibt auch n paar die ihre Kugel zum Upgraden gegeben haben ... aber das dauert bekanntlich 4 - 6 Wochen*
 
Hallo,

ich kann leider kein lauffähiges Code-Beispiel einstellen, weil ich mit größeren Frameworks arbeite. Ich kann auch wirklich ausschließen, dass es am File Lock liegt. Ich dachte, dass man das Problem "konzeptuell" also ohne viel Code lösen könnte. Ich beschreibe nochmal genauer, was passiert:
In meinem Hauptprogramm passiert in einem Durchlauf folgendes:
1. Ich generiere ein Java-Programme (Client), konkret: eine Klasse als .java-Datei. - funktioniert einwandfrei
2. Ich kompiliere diese Klasse über die Konsole per Command über Runtime.getRuntime().exec(...); und warte per waitFor() bis die Kompilierung abgeschlossen ist. - funktioniert einwandfrei
3. Ich starte den Server per Runtime.getRuntime().exec(...); - funktioniert einwandfrei
4. Ich starte den eben kompilierten Client. - funktioniert
5. Nun muss ich darauf warten, dass der Client beendet wird oder er eine Datei erzeugt hat (hier: äquivalente Ereignisse bei fehlerfreier Ausführung!)

Das Warten - egal in welcher Weise - blockiert dann den Client. Ich habe folgende Vermutung: das waitFor() beim Kompilieren funktionierte, weil javac (Compiler) ein anderes Programm ist als java (JRE). Der Client läuft aber wie das Hauptprogramm unter java (JRE). Und wenn ich dann java (JRE) warten lasse, wartet eben alles. Ich vermute weitehin, dass eine Lösung möglich wäre, wenn man die Ausführung des Clients als zusätzlichen Prozess oder Thread realisiert, aber ich weiß nicht wie. Per Process wird offenbar nur ein Unterprozess erzeugt, kein paralleler.

Vorschläge?
 
Hi,

könntest Du bitte mal die Zeile posten, die Du zum Starten des Clients (P2) verwendest.

Bzw. hast Du schonmal getestet, ob es funktioniert, wenn Du den Client nicht innerhalb Deines Programms startest, sondern manuell. Vielleicht klemmt dort noch etwas.

Gruß twagi
 
Zuletzt bearbeitet:
Hallo,

das ist die im ersten try-Block im Code-Beispiel meines ersten Beitrages. Alternativ habe ich auch
Code:
processController = Runtime.getRuntime().exec(commandRunClient);
versucht. Macht aber keinen Unterschied. Das übergebene Kommando ist jeweils "java -cp <CLASSPATH> <anypackage>.Client" und funktioniert.

Grüße,
Toni
 
Hi,

Du startest eine neue JVM, also einen neuen Prozess. Daher sollte es keine Auswirkungen haben, wenn Du in der einen VM auf ein Ergebnis aus der anderen wartest.

Wenn Du den Client so startest wird die Datei erzeugt?

Gruß twagi
 
Hi,

nein, dazu kommt der Client nicht, weil er offenbar blockiert wird. Der Server meldet in dem Fall wie gesagt "Client timeout". Wenn ich im Hauptprogramm nicht auf den Client/die Datei warte, sondern mit Dummy-Ergebnissen weitermache, dann wird der Client ausgeführt und der Server meldet kein "Client timeout". Das Warten auf den Client blockiert wohl selbigen.

Grüße,
Toni
 
Wie wäre es dann mal mit Debuggen ? Also in den Clienten wirklich jede Anweisung mit sowas umschließen:

Java:
System.out.print("EXECUTE COMMAND : ");
//command
System.out.println("OK");

Dann kannst du nämlich genau sehen WO er genau hängt ... denn die Fehlermeldung vom Server sagt eigentlich nichts aus außer das der Socket irgendwie zusammenbricht.
 
Zurück