2015-09-05 5 views
2

Почему имя пользователя не напечатано, когда я комментирую println("testing")?Asynchronous in Failure

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.resultиonSuccess обратного вызова, он все еще может случиться, что основные рутинные выходы первого и не выход пользователя будет, поскольку нет никакого порядка, какой из оба делаются первыми.

Длинный ответ

Посмотрите на код

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, как потоки демона.

Во-вторых, сначала выполняется основная работа, печать «main ждет обещания» и ждет здесь (только для целей демонстрации) для выполнения обещания. Если основной не стал бы ждать здесь (попробуйте сами, комментируя Await) для завершения обещания, основная процедура просто напечатает две другие строки и будет выполнена.В результате, виртуальная машина будет закрыть (и вы никогда не увидите выход onComplete)

Trick (для отладки) с помощью SBT

В общем, если вы используете SBT и вызвать выполнение программы с помощью run, то вам все еще может видеть вывод потоков демона, поскольку JVM не завершается, если он запущен из SBT. Итак, если вы начнете с SBT run, вы скоро вернетесь к приглашению SBT (потому что основная процедура закончилась), но выход SBT (onComplete) отображается в SBT.

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)