2012-05-12 4 views
9

Ich habe versucht, das Beispiel auf neuen Scala 2.10 futures feature zu reproduzieren. Der Code, den ich verwendet habe, ist:Möglicher Fehler in Scala 2.10: Futures laufen nicht

import scala.concurrent.Future 
import scala.concurrent.future 

object Test { 
    def main(args: Array[String]) { 
    println("Test print before future") 
    val s = "Hello" 
    val f = future {s + " future!"} 
    f onSuccess {case v => println(v)} 
    println("Test print after future") 
    } 
} 

Statt Druck:

Test print before future 
Hello future! 
Test print after future 

Es einfach druckt:

Test print before future 
Test print after future 

Jede Idee, warum ich dieses Verhalten haben? Meine Version des Scala-Compilers ist 2.10.0-20120507.

Antwort

28

Das Problem ist, dass Sie ausführen, dass als eigenständiges Programm, dessen Haupt Thread wird beendet, bevor einer der Worker-Threads die "Hallo Zukunft!" ausführen kann println. (Die Threads, die die neue Futures-Bibliothek erzeugt, sind Daemon-Threads).

Sie können auch verwenden, um die Await Objekt (auch in scala.concurrent) zu warten, bis die Zukunft f abgeschlossen ist:

import scala.concurrent._ 
import scala.concurrent.util._ 

object Test { 
    def main(args: Array[String]) { 
    println("Test print before future") 

    val s = "Hello" 
    val f = future {s + " future!"} 
    f onSuccess {case v => println(v)} 
    println("Test print after future") 

    Await.ready(f, Duration.Inf) 
    } 
} 

Dieser Druck kann:

Test print before future 
Test print after future 
Hello future! 

Oder es kann drucken „Hallo Zukunft!" vor "Test print after future" abhängig vom Thread-Zeitplan.

Ebenso können Sie den Haupt-Thread zwingen, zu warten, bis f vor dem letzten println wird wie folgt ergänzt:

import scala.concurrent._ 
import scala.concurrent.util._ 

object Test { 
    def main(args: Array[String]) { 
    println("Test print before future") 

    val s = "Hello" 
    val f = future {s + " future!"} 
    f onSuccess {case v => println(v)} 

    Await.ready(f, Duration.Inf)   

    println("Test print after future") 
    } 
} 

Welche drucken würde:

Test print before future 
Hello future! 
Test print after future 

Beachten Sie jedoch, dass, wenn Sie verwenden Await, Sie blockieren. Dies macht natürlich Sinn, um sicherzustellen, dass Ihr Hauptanwendungs-Thread nicht beendet wird, aber im Allgemeinen sollte nicht verwendet werden, es sei denn, es ist notwendig. Das Objekt ist eine notwendige Escape-Luke für Situationen wie diese, aber die Verwendung des gesamten Anwendungscodes ohne Rücksicht auf seine Semantik kann zu einer langsameren, weniger parallelen Ausführung führen Wenn Sie sicherstellen müssen, dass Callbacks in einigen Fällen ausgeführt werden B. die Reihenfolge andThen und map auf Future.)

+0

Sie wissen? Ich dachte darüber nach, weil es genau so ist, wenn man mit Goroutines zu tun hat: Man benutzt Kanäle, um auf eine Nachricht zu warten, die von den Goroutines gesendet wird, bevor der Hauptblock geschlossen wird. –

+0

@Heather Kann man auf mehr als eine Zukunft warten? – 66CLSjY

1

Ich denke, dass das Problem hier Timing ist. Wahrscheinlich läuft Ihr zukünftiger Code in einem separaten Deamon-Thread. Ich denke, dass die Anwendung sehr schnell beendet wird und dieser Deamon-Thread nicht genug Zeit hat, um korrekt ausgeführt zu werden (die Anwendung wartet nicht darauf, dass Deamon-Threads beendet werden). Aber auch dieses sehr systemabhängige Verhalten. Für mich druckt es:

Test print before future 
Test print after future 
Hello future! 

und dann beendet (ich benutze Scala 2.10.0-M3). Sie können folgende versuchen, um es zu testen - nur Haupt-Ausführungs-Thread für einige Sekunden in Schlaf versetzt und sehen, ob Hello future! gedruckt wird:

import scala.concurrent.Future 
import scala.concurrent.future 

object Test { 
    def main(args: Array[String]) { 
     println("Test print before future") 

     val s = "Hello" 
     val f = future {s + " future!"} 
     f onSuccess {case v => println(v)} 

     println("Test print after future") 

     Thread.sleep(3000) 
     println("Test print at the end.") 
    } 
} 
1

Ich möchte nur hinzufügen, dass im Allgemeinen gibt es eine andere Möglichkeit, dass Futures nicht ausgeführt werden: Hitting die Thread-Pool-Grenze.

In Ihrem Fall war es wohl einfach ein Timing-Problem wie andere haben darauf hingewiesen, sondern als zukünftige Referenz dieses Beispiel betrachten:

import scala.concurrent._ 
import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.duration.Duration 


object FutureDebug { 
    def main(args: Array[String]) { 

    for (i <- Range(0, 4)) { 
     future { 
     while (true) { 
      Thread.sleep(1000) 
      println("I'm doing stupid things in a future") 
     } 
     } 
    } 

    println("(1) reached? yes") 
    val fut = future { 
     for (i <- Range(0, 1000)) { 
     println("never reached " + i) 
     } 
     3.14 
    }  
    println("(2) reached? yes") 
    Await.result(fut, Duration.Inf) 
    println("(3) reached? no") 
    } 
} 

Auf meinem Rechner des globalen Standardausführungskontext hat nur 4 Threads. Da die Worker-Threads damit beschäftigt sind, die 4 Nicht-Sinn-Futures auszuführen, wird die Zukunft darunter nie ausgeführt. Aus diesem Grund sollte man mit dem Standardausführungskontext vorsichtig sein und es ist am besten, specify one's own execution context im Umgang mit mehreren (wirklich) lang laufenden Futures.