Threads managen / Schleife fortsetzen, wenn Thread beendet

02/01/2015 20:15 Zunft#1
Hallo zusammen,

mein Problem ist folgendes:

Ich habe in einer ForEach-Schleife 3 Threads, die alle gleichzeitig ausgeführt werden müssen, allerdings auch jeweils nur diese 3 gleichzeitig. In der Schleife ist es aber so, dass hier dann alle "3er-Pakete" so oft ausgeführt werden, wie die Schleife es angibt.

Also zusammengefasst: Ich möchte die Schleife erst dann fortsetzen, wenn alle Threads beendet sind, aber keine weitere Schleife á la:

Code:
while(!finish)
{

}
hinzufügen, da sich so die GUI aufhängt.

Code:
Task progress = new Task<Void>() {
            @Override
            public Void call() {

                this.updateProgress(dwtable.getItems().size(), 1);
                
                while (ydown.Percent < 100) {
                    if (isCancelled()) {
                        break;
                    }
                    setPercent(null, ydown.Percent);
                    updateProgress(ydown.Percent, 100);
                                        
                    System.out.println("Prozent: " + ydown.Percent);
                    
                }
                updateProgress(100, 100);
                ydown.Percent = 0;

                return null;
            }
        };

        Task download = new Task<Void>() {
            @Override
            public Void call() {

                ydown.download(dwtable.getItems().get(zaehler).getYouTube());
                zaehler++;
                finish = true;
                return null;
            }
        };

        for(Track tr : dwtable.getItems()) 
        {
                        
            try {
                executor.execute((Runnable) tr);
                testProg.progressProperty().bind(progress.progressProperty());

                executor.execute(download);
                executor.execute(progress);
                finish = false;
               
                
            } catch (Exception ex) {
                Logger.getLogger(MainFrameController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
und in Track:

Code:
    @Override
    protected Void call() throws Exception
    {
        
        mfc = new MainFrameController();
        this.updateProgress(ProgressIndicator.INDETERMINATE_PROGRESS, 100);
        
        while(percent < 100)
        {
            updateProgress(percent, 100);
        }
        
        updateProgress(100, 100);
        percent = 0;
        
        return null;
    }
02/01/2015 21:56 XxharCs#2
Verwende ThreadPoolExecutor und sogenannte "Watchdogs".

Glücklicherweise habe ich auf meinen Github Acc ein Programm welches sehr stark mit Threads arbeitet, und da werden ThreadPoolExecutoren und Watchdogs verwendet.

Wirf ein Blick rein, vielleicht wird es dir helfen: [Only registered and activated users can see links. Click Here To Register...]
Und das Projekt generell: [Only registered and activated users can see links. Click Here To Register...]


Und was ich von deinem Code herleiten konnte, willst du eine Progressbar(und andere Progress änderungen) darstellen oder? Ich würde dann das Observer verwenden(Observer Nutzung auch auf meinem Git Acc: [Only registered and activated users can see links. Click Here To Register...]) :)
02/01/2015 22:08 Zunft#3
Quote:
Originally Posted by XxharCs View Post
Und was ich von deinem Code herleiten konnte, willst du eine Progressbar(und andere Progress änderungen) darstellen oder? Ich würde dann das Observer verwenden(Observer Nutzung auch auf meinem Git Acc: [Only registered and activated users can see links. Click Here To Register...]) :)
Prinzipiell ja, hier geht es aber nicht um eine einzige Progressbar sondern um mehrere ProgressBars, die in einer TableView sind.

[Only registered and activated users can see links. Click Here To Register...]
02/01/2015 23:02 XxharCs#4
Ich werde mich morgen hier nochmal melden/diesen Beitrag editieren mit einer Lösung, weil ich schon eine Idee habe :D (Natürlich falls sich keiner schon vor mir gemeldet hat :p)

Edit: Ohne das du jetzt groß dein Design änderst, und sehe ja das dein Code den ExecuterService(nice! :D) verwendet, müsstest du trotzdem eine while-Schleife reinhauen :/
Code:
executor.shutdown(); // den ExecuterService schließen nachdem du Threads mit ihm gestartet hast
	
while (!executor.isTerminated()) {} // wartet bis alle Thread fertig sind
Bei dem Observer Prinzip müsstest du in diesem Fall etwas mehr Code ändern :/ Lass mich wissen falls du es doch anders haben möchtest, und ich schreibe ne kurze Application die mit mehreren Progressbars arbeitet und das Obserer Prinzip verwendet :)

Edit2: Obwohl wenn du das Observer Pattern anwendest, müsstest du auch irgendwann mal nachfragen ob progress jetzt 100% ist oder so, zwar ist man da Flexibler wie zb if(progress == 100) dann enable wieder diese bestimmten Funktionen oder so.
Was genau sollte denn passieren nachdem die Threads fertig sind? Sind dann bestimmte Funktionen wieder zur Verfügung gestellt oder wie?
02/02/2015 14:11 Zunft#5
Ich werde nachher den kompletten relevanten Code einstellen. Der Code ist allerdings !noch! ziemlich unübersichtlich und noch nicht wirklich Objektorientiert.

Also das Prinzip ist folgendermaßen - hoffe es ist halbwegs verständlich dargestellt:

-> Downloadknopf gedrückt
-> Für jeden Track (Musikstück) in TableView
--> Lade Track herunter
--> Update Gesamtprogressbar (Alle Tracks) anhand (noch nicht implementiert, testweise lediglich Fortschritt von einzelnem Track)
--> Update jeweilige Progressbar des Tracks im TableView
--> Wenn Track heruntergeladen, starte Download des nächsten Tracks
--> GUI soll weiterhin aktiv bleiben ohne zu blockieren

*Edit: Das sollte alles relevantes sein. Ich muss mich leider beeilen, daher kann es sein, dass etwas fehlt.

MainFrameController:

Track:

Youtube:
02/02/2015 15:25 XxharCs#6
Asooo, ja das kann man auf 2 Arten machen, die eine Art mit join() und die andere ivokeAll() oder submit().

Bei der 1 Art:
Die Threads in ein Array packen, sie starten und dann mit einer Schleife für die Threads ein join(), also:
Code:
for(i = 0; i < threads.length; i++)
    threads[i].join();
Der Join wartet bis der Thread vor ihm fertig ist und startet den. Die Reihenfolge ist nicht garantiert^^

Die 2 Art:
Mit Future Objekt arbeiten und der Methode invokeAll() vom ThreadPoolExecutor. Dieses Future Objekt beinhaltet dann den Status des/(r) Threads.
Code:
List<Future<Runnable>> futures = new ArrayList<Future<Runnable>>();
// in deiner for-each jetzt
Future fTrack = executor.submit(tr); // oder submit((Runnable)tr); habs ned getestet
futures.add(fTrack);

for(Futures<Runnable> f : futures) {
    f.get(); // was passiert in dieser for-Schleife? Es wird ein Track nach dem anderen abgearbeitet (also wenn eins fertig ist dann das nächste)
}
Ich würde zusätzlich sagen, dass in deiner Tracks Klasse, deine download und progress Tasks ausgeführt werden sollten.


Edit: Mit der 2 Art selbst habe ich mich bis jetzt noch nicht wirklich beschäftigt(also was das starten des nächsten Threads wenn der jetzige Thread beendet wurde, angeht), deshalb meine recherche dazu:
[Only registered and activated users can see links. Click Here To Register...]
[Only registered and activated users can see links. Click Here To Register...]
[Only registered and activated users can see links. Click Here To Register...]
[Only registered and activated users can see links. Click Here To Register...]

Ich hoffe das hilft dir weiter! :)
02/02/2015 17:19 Zunft#7
Danke, werde ich nachher ausprobieren wenn ich zuhause bin.

Bzgl. Der ersten methode:
Wenn ich die threads in ein arrax packe und diese mit einer schleife ausführe werden doch dann alle threads nacheinander ausgeführt also erst der download und danach die progressbar und nicht beide gleichzeitig oder ? Das heißt ich müsste entweder drei schleifen anlegen oder die threads in den einzelnen threads verschachteln also in download thread den progress thread aufrufen oder sehe ich das falsch ?

Edit: rein theoretisch sollte sich die GUI bei der methode auch aufhängen, das heißt die schleife(n) müssten auch wiederum in einem weiteren thread ausgelagert werden. Da sieht die zweite methode wesentlich eleganter aus, wobei auch hier so wie ich das sehe, ein weiterer thread erforderlich ist. Aber wie gesagt : ich probiere es nachher aus und melde mich nochmal.
02/02/2015 20:17 XxharCs#8
Ja also, wie mein letzter Satz lautete vor dem Edit:
Quote:
Ich würde zusätzlich sagen, dass in deiner Tracks Klasse, deine download und progress Tasks ausgeführt werden sollten.
Weil eigentlich müssten die Prozente nicht mehr stimmen wenn du das in 3 verschiedenen Threads machst. Du kannst nicht garantieren ob jetzt dein "download" oder "progress"-Task zuerst fertig sein wird, mache das am besten in der Track Klasse oder in der Youtube Klasse und von dort aus benachrichtigst du deine GUI das sich was geändert hat und somit die Progressbar ändert. (Da würde eigentlich das Observer Pattern/Prinzip ins Spiel kommen)
02/02/2015 21:31 Zunft#9
Vielen Dank für deine Hilfe ich habe es jetzt vorübergehend mit der 1. Methode gelöst, der Download funktioniert noch nicht optimal, das ist aber ein anderes Problem.

Für diejenigen, die an der Lösung interessiert sind, hier meine Lösung als Beispiel:

Tracks.java
Code:
    @Override
    protected Void call() throws Exception
    {
        updateProgress(100, 100); //Progressbar füllen
        percent = 0;
        Thread.sleep(1000); //1 Sekunde warten
        
        return null;
    }
Controller.java
Code:
Runnable[] threads = new Runnable[dwtable.getItems().size()]; //Lege ein Array für die Runnables aus Tracks an

Task startprocess = new Task<Void>() {
            @Override
            public Void call() {
                
                for(int i = 0; i < threads.length; i++)
                {
                    Thread thread1 = new Thread((Runnable) threads[i], "Track"); //Es wird nun für das Runnable ein Thread angelegt
                    try {          
                        thread1.start(); //Thread starten
                        thread1.join(); //Warten, bis dieser beendet wurde
                        
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
                    
                return null;
            }
        };

int z = 0;

for(Track tr : dwtable.getItems()) 
        {
                        
            try {

                System.out.println("Füge Thread " + z + " zu Array hinzu");
                
                threads[z] = (Runnable) tr;
                
                z++;
               
                
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
02/02/2015 21:57 snow#10
Danke für das Posten deiner Lösung. :)

#closed (on request)