Threads Kann mir jemand helfen eine parallele Hilfsklasse zu implementieren..?

sirbender

Top Contributor
Hallo,

ich will eine langwierige Rechnung parallelisieren. Ich habe viel mit ExecutorService und den entsprechenden ThreadPools experimentiert. Aber so richtig bekomme ich damit nicht hin was ich will. Ich weiss vor allem nicht wie man den TreadPool sinnvoll steuert (aktive Threads schlafen legen usw.)

Ich habe mal ein kleines Codebeispiel geschrieben. Ich nutze int[] als Input und Integer als Resultat. Diese Datenstrukturen dienen nur als Beispiel.

Ich denke mit dem Codebeispiel wird klarer was ich vor habe:

Java:
package experiment;

import java.util.HashMap;
import java.util.Map;

public class ParallelHelperExample {

    public static void main(String[] args) {
        Map<String, int[]> dataMap = new HashMap<String, int[]>();
        dataMap.put("AcceleratorCollisionDataSet-2015-05-25", new int[] { 1, 2, 3 });
        dataMap.put("AcceleratorCollisionDataSet-2015-05-26", new int[] { 4, 5, 6 });
        dataMap.put("AcceleratorCollisionDataSet-2015-05-27", new int[] { 7, 8, 9 });
        // ... typischerweise 200 dataSets

        ParallelHelper helper = new ParallelHelper(dataMap) {
            @Override
            Integer calcResult(int[] data) {
                return 0; // die Berechnung ist extremst kompliziert und langwierig (Stunden)
            }
        };
       
        helper.start();
       
        // danach sporadische Aufrufe von helper.getResult(mapKey). Der Wert von 'mapKey' ist nicht vorhersagbar
        // und somti auch die Reihenfolge der Berechnung der Resultate nicht optimierbar 
    }
}

abstract class ParallelHelper {
    final Map<String, Integer> resultMap = new HashMap<String, Integer>();
   
    public ParallelHelper(Map<String, int[]> dataMap) {
        // nutzt eine Art Threadpool, z.B. via ExecutorService
    }
   
    void start() {
        // started die Berechnung fuer jedes key-value Paar in 'dataMap' ein Ergebnis via calcResult(...)
        // speichert das Ergebnis in 'resultMap'
    }
   
    // calcResult muss von Nutzer von ParallelHelper implementiert werden
    abstract Integer calcResult(int[] data);
   
    int getResult(String mapKey) {
        // schaut ob das Ergebnis fuer 'mapKey' bereits berechnet wurde
        // muss das Ergebnis noch berechnet werden, nutzt ParallelHelper nun 100% der Rechenleistung um das Ergbnis fuer 'mapKey' zu generieren
        // dazu muessen alle aktiven Threads im Pool die gerade Ergebnisse fuer andere 'mapKeys' berechnen 'schlafen gelegt' bzw. 'gestoppt' werden
        // Anmerkung: es waere gut, wenn ParallelHelper hier die bereits laufenden Berechnungen nur anhalten und spaeter wieder fortsetzen
        // koennte anstatt komplett stoppen und neu beginnen. Dies liegt daran, dass einzelne Berechnungen teilweise Stunden brauchen.
       
        // Sonderfall: der gesuchte mapKey wird bereits von einem der aktiven Threads berechnet.
        // In diesem Fall soll dieser eine Thread natuerlich weitermachen und nicht 'schlafen gelegt' werden ;)
       
        // wurde das gesuchte Ergebnis berechnet sollten die vorher 'schlafen gelegten' bzw. 'gestoppten' Threads
        // wieder automatisch weiterarbeiten bzw. 'neu gestartet' werden bis 'resultMap' vollstaendig gefuellt ist
       
        return 0; // das gesuchte Ergebnis wird zurueckgegeben
    }
}
 
Also ich würde z.B. keine get-Methode benutzen, um einen Wert abzufragen. Ich würde mir ein Interface definieren mit z.B. public void calculationFinished(String key, int result). Das Interface jedem Thread z.B. im Konstruktor übergeben. Dein "ThreadManager" würde dann die Events abfangen und in die "ResultMap" reinschreiben. So mußt du nicht explizit einen Wert anfragen sondern bekommst ihn mitgeteilt, sobald der Wert berechnet wurde.

Deine getResult-Methode klingt für mich der Beschreibung nach eher nach sowas wie einem "force". Es sollte auch kein Problem sein, über eine Variable andere Threads zu pausieren, bis ein bestimmter Thread seine Arbeit erledigt hat. Z.B. könnte man jedem Thread einen Namen geben (z.B. der Key der Map) und um einen Thread zu forcieren kannst du in der run-methode abfragen, ob der Thread-Name paßt, ansonsten pausieren bzw. bei NULL laufen alle Threads weiter oder so in der Art.

Desweiteren kannst du bei einem Thread auch eine Priority setzen, um bestimmte Threads zu bevorzugen oder zu benachteiligen (MIN, MAX, von 1 - 10). Ob dir das was bringt, weiß ich nicht. Weiß auch nicht, ob man die Prio während der Thread läuft verändern kann. Müßte man mal ausprobieren.

Ich nutze selbst oft bei Threads oben genannte "Interface-Methode" bzw. "Listener-Prinzip" (funktioniert quasi wie die ActionListener usw.) und komme damit gut klar. Gibt aber sicherlich auch andere Möglichkeiten, deine Aufgabe zu lösen.
 
Danke fuer die Antwort.

Das explizite Wert-Abfragen MUSS aber moeglich sein. Es geht darum, dass der ThreadPool gestartet wird und im Hintergrund die Ergebnisse berechnet. Es kann sein, dass ein Ergebnis (mittels des mapKey) abgefragt wird. Dann soll wie ich in den Comments geschrieben habe der Parallelhelper-Threadpool alle anderen Berechnungen erstmal sein lassen und nur mit einem Thread das abgefragte Result berechnen. Ist er damit fertig und hat es zurueckgegeben, kann die Taetigkeit der anderen Threads fortgesetzt werden.

Der Knackpunkt ist halt das Pausieren der Threads, der Berechnen des Werts und das Fortsetzen der Threads. Und dann sollten halt auch die Sonderfaelle behandelt werden, naemlich, dass der Wert der angefordert wird gerade berechnet wird bzw. dass ein zweiter oder dritter Wert angefordert wird waehrend der erste angeforderte Wert noch nicht zurueckgegeben wurde. Das sollten den ParallelHelper alles nicht ueberfordern und zu Problemen fuehren.

Du schreibst von Variablen usw. mit denen man pausieren kann. Das weiss ich natuerlich alles aber in allen meinen Versuchen fuehrte das zu allen moeglichen Desastern und Problemen. Hier hoffe ich, dass mir jemand an einen minimalen, konkreten Beispielcode zeigen kann wie sowas narrensicher geht. Das Drumherum schaffe ich dann hoffentlich selbst.
 

Zurück
Oben