2013-04-04 9 views
9

Mein aktueller Code verwendet Reihe von asynchronen Prozessen, die in Ergebnissen enden. Ich muss jede davon so umhüllen, dass auf jede von einer synchronen Methode mit dem Ergebnis als Rückgabewert zugegriffen wird. Ich möchte die Executor-Dienste dazu nutzen, um viele von ihnen gleichzeitig zu ermöglichen. Ich habe das Gefühl, dass Future für meine Implementierung relevant sein könnte, aber ich kann keinen guten Weg finden, dies zu erreichen.Wickeln einer Reihe von asynchronen Aufrufen mit einer synchronen Methode mit einem Rückgabewert

Was ich habe jetzt:

public class DoAJob { 
    ResultObject result; 

    public void stepOne() { 
    // Passes self in for a callback 
    otherComponent.doStepOne(this); 
    } 

    // Called back by otherComponent once it has completed doStepOne 
    public void stepTwo(IntermediateData d) { 
    otherComponent.doStepTwo(this, d); 
    } 

    // Called back by otherComponent once it has completed doStepTwo 
    public void stepThree(ResultObject resultFromOtherComponent) { 
    result = resultFromOtherComponent; 
    //Done with process 
    } 
} 

Diese ziemlich gut intern gearbeitet hat, aber jetzt brauche ich, wie mein Prozess in eine synchrone Methode mit einem Rückgabewert zuzuordnen:

public ResultObject getResult(){ 
    // ??? What goes here ??? 
} 

Hat jemand Haben Sie eine gute Idee, wie Sie das elegant umsetzen können?

+1

In dieser Spezifikation fehlt eine Sache: Wie wollen Sie das ResultObject der verschiedenen Aufgaben am Ende zu einem einzigen kombinieren? –

+0

Ich bin mir nicht sicher, ob ich die Frage verstehe. Normalerweise wird der Prozess durch neue DoAJob.stepOne() gestartet; Die Methode stepTwo() wird durch einen Callback von otherComponent initiiert. Am Ende der Kette von Callbacks ist das ResultObject korrekt ausgefüllt. Irgendwelche Zwischendatenbits werden in das Endergebnis integriert. – irondwill

Antwort

0

I invokeAll(..) empfehlen. Es wird eine Reihe von Aufgaben an den Executor übergeben und blockiert, bis der letzte abgeschlossen ist (erfolgreich/mit Ausnahme). Es gibt dann eine Liste der abgeschlossenen Future-Objekte zurück, sodass Sie sie erneut durchlaufen und die Ergebnisse in ein einzelnes ResultObject zusammenführen können.

In Sie nur eine einzige Aufgabe in einer synchronen Weise ausführen möchten, können Sie das folgende verwenden:

executor.invokeAll(Collections.singleton(task)); 

--edit--

Jetzt denke ich, dass ich besser verstehen Ihre Bedürfnisse. Ich nehme an, dass Sie eine Möglichkeit benötigen, unabhängige Aufgabenfolgen zu übermitteln. Bitte werfen Sie einen Blick auf den Code, den ich in this answer veröffentlicht habe.

+0

Ich habe das in meinem Beispiel nicht gut dargestellt, aber die Schritte hängen davon ab, ob der vorherige Schritt abgeschlossen wurde. Zum Beispiel wird stepTwo() bei der Remote-Vervollständigung von stepOne() ausgeführt. stepTwo() ist im Wesentlichen der Abschluss-Callback für die in stepOne() aufgerufene Komponente – irondwill

7

Wenn Sie eine asynchrone Operation (die einen Rückruf ausführt, wenn sie beendet ist) in eine synchrone/blockierende Operation umwandeln möchten, können Sie eine blockierende Warteschlange verwenden. Sie können dies bei Bedarf in ein Future-Objekt einfügen.

  1. eine blockierende Warteschlange definieren, die nur ein Element halten kann:

    BlockingQueue<Result> blockingQueue = new ArrayBlockingQueue<Result>(1);

  2. Starten Sie Ihren asynchronen Prozess (wird im Hintergrund ausgeführt werden), und schreiben Sie den Rückruf, dass, wenn es fertig ist, Es fügt sein Ergebnis der blockierenden Warteschlange hinzu.

  3. In Ihrem Vordergrund/Anwendungs-Thread, haben sie() aus der Warteschlange nehmen, die blockiert, bis ein Element zur Verfügung steht: (Vordergrundthread Bedürfnisse etwas Ähnliches schrieb

    Result result = blockingQueue.take();

ich vor um eine asynchrone Antwort von einem Remote-Rechner zu blockieren.) Verwenden Sie etwas wie eine Zukunft, finden Sie Beispielcode here.

+0

Eine 'SynchronousQueue' ist wahrscheinlich besser für diesen Zweck – ZhongYu

+2

Ich war dort, bevor ich Angst habe (mit dem Code, mit dem ich tatsächlich verbunden bin). Eine SynchronousQueue kann dazu führen, dass der Hintergrund-Thread eine Exception blockiert oder eine Exception empfängt, wenn eine Racebedingung vorliegt, bei der der Hintergrund-Thread vor dem Vordergrund-Thread namens take() abgeschlossen wurde. Wenn dies in einer Zukunft verpackt war, wie das OP erwähnt hat, könnte es ein häufiges Vorkommen sein, wenn die Anwendung vor dem Aufruf von Future.get() etwas anderes getan hat. – npgall

0

Wenn Sie Ihre Hände schmutzig machen, können Sie diese

ResultObject result; 

public void stepOne() 

    otherComponent.doStepOne(this); 

    synchronized(this) 
     while(result==null) this.wait(); 
    return result; 

public void stepThree(ResultObject resultFromOtherComponent) 

    result = resultFromOtherComponent; 

    synchronized(this) 
     this.notify(); 

tun Oder können Sie höhere Ebene Concurrency-Tools verwenden, wie Blocking, Semaphore, CountdownLatch, Phaser, etc etc.

Hinweis dass DoAJob nicht threadsicher ist - Probleme treten auf, wenn zwei Threads gleichzeitig stepOne aufrufen.

+0

Ja, ich werde vielleicht so etwas machen müssen, da es sich um einen ziemlich spezifischen Anwendungsfall und eine Architektur handelt. Ich hatte gehofft zu vermeiden, dass ich wait() Schleifen implementieren musste oder meinen Code mit zu vielen synchronisierten() Aufrufen verwarf. – irondwill

Verwandte Themen