2015-09-05 152 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)