2015-09-05 150 views
2

爲什麼我在註釋掉println("testing")時未打印用戶名?異步異步

import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.Future 

object Future3 extends App { 
    val userFuture = Future(
    User("Me") 
) 
    val userNameFuture: Future[String] = userFuture map { 
    user => user.name 
    } 

    userNameFuture onSuccess { 
    case userName => println(s"user's name = $userName") 
    } 

    // println("testing") 
} 

case class User(name: String) 
+2

http://stackoverflow.com/questions/31900681/the-future-is-not-complete –

+0

如果你刪除了'應用程序'它按預期工作。 – Jus12

回答

0

簡短的回答

ExecutionContext.Implicits.global創建守護線程。 (請參閱Scala源代碼scala.concurrent.impl.ExecutionContextImpl.DefaultThreadFactory)這些是JVM在退出時不會等待的線程(在您的情況下,主例程停止時)。因此,在作爲守護程序線程運行的userNameFuture完成之前,主例程已經完成,並且不會等待以便未來的線程完成。

爲防止發生這種情況,可以使用非守護線程(例如,創建這樣的隱式ExecutionContext

implicit val ec = (scala.concurrent.ExecutionContext.fromExecutorService(Executors.newCachedThreadPool())) 

,或者使用

Await.result(userNameFuture, Duration.Inf) 
在主程序

注意:如果使用後一種方法與Await.resultonSuccess回調,但仍可能發生,首先主程序退出,沒有用戶名的輸出將被製成,因爲沒有訂單,其中首先發生。

龍答案

看一看代碼

object F2 { 

    def main(args: Array[String]): Unit = { 

    import scala.concurrent.ExecutionContext.Implicits.global 
    import scala.util.Success 

    val userFuture = Future { 
     Thread.sleep(1000) 
     println("userFuture runs on: " + Thread.currentThread().getName) 
     Thread.sleep(1000) 
     User("Me") 
    } 

    val userNameFuture: Future[String] = userFuture map { 
     user => { 
     Thread.sleep(2000) 
     println("map runs on: " + Thread.currentThread().getName) 
     Thread.sleep(2000) 
     user.name 
     } 
    } 

    val p = Promise[Boolean]() 

    userNameFuture onSuccess { 
     case userName => { 
     println("onSuccess runs on : " + Thread.currentThread().getName) 
     println(s"user's name = $userName") 
     p.complete(Success(true)) 
     } 
    } 


    println("main runs on: " + Thread.currentThread().getName) 
    println("main is waiting (for promise to complete) .... ") 
    Await.result(p.future, Duration.Inf) 
    println("main got promise fulfilled") 
    println("main end ") 

    } 
} 

其輸出

main runs on: run-main-b 
main is waiting (for promise to complete) .... 
userFuture runs on: ForkJoinPool-1-worker-5 
map runs on: ForkJoinPool-1-worker-5 
onSuccess runs on : ForkJoinPool-1-worker-5 
user's name = Me 
main got promise fulfilled 
main end 

首先,你可以看到,這兩個userFuture並作爲ForkJoinPool它的地圖操作運行守護進程線程。

二,主要通過首先運行,打印「主要等待承諾」並在此等待(僅用於消除目的)以實現承諾。如果主要不會在這裏等待嘗試自己,通過評論出Await)承諾完成,主例程將只打印其他兩行並完成。其結果是,在JVM將關閉(你永遠不會看到的onComplete輸出),通過SBT

絕招(調試)

在一般情況下,如果你正在使用SBT,並通過run調用程序的執行,然後你仍然可以看到守護進程線程的輸出,因爲如果從SBT內部啓動,JVM不會終止。 因此,如果通過SBT run啓動,則很快會返回到SBT提示符(因爲主例程已完成),但在SBT中可以看到線程的輸出(onComplete)。

2

的原因是,默認情況下ExecutionContextglobal執行你的未來的守護線程和主線程塊不等待守護進程來完成。您可以在主線程中使用Thread.sleep(1000)Await.result(userNameFuture, 1 second)或其他線程阻塞操作等待一段時間,以便將來的線程完成。

另一種方式是在沒有守護線程運行的未來:

進口java.util.concurrent.Executors

import scala.concurrent.{ExecutionContext, Future} 

object Future3 extends App { 

    implicit val executor = ExecutionContext 

    .fromExecutorService(Executors.newCachedThreadPool()) //not-daemon threads 


    val userFuture = Future(
    User("Me") 
) 
    val userNameFuture: Future[String] = userFuture map { 
    user => user.name 
    } 

    userNameFuture onSuccess { 
    case userName => println(s"user's name = $userName") 
    } 

} 

case class User(name: String)