2016-06-21 2 views
-3

Что я хочу сделать, это использовать Future, чтобы открыть поток для обработки задачи async, которая может быть вызвана часто.Scala Будущее не работает, как я ожидаю

Но в async task. Я также вызываю две функции Future, чтобы получить информацию из разных источников данных.

Я пишу программу для имитации этой ситуации.

import scala.concurrent.{ExecutionContext, Future} 
import scala.util.{Success, Failure} 
import scala.util.control.Breaks 
import ExecutionContext.Implicits.global 
object AsyncTest { 


    def main(args: Array[String]) { 
    try { 
     println("Run...") 
     aSyncTask 
    } catch { 
     case e => e.printStackTrace() 
    } 

    Thread.sleep(999999) 
    } 
    //dataSource1 
    def getInfo1 : Future[String] = Future{ 
    "A" 
    } 
    //dataSource2 let it wait 3 seconds... 
    def getInfo2 : Future[String] = Future{ 
    Thread.sleep(3000) 
    "B" 
    } 

    def aSyncTask = Future{ 
    getInfo1 
    getInfo2 

    var result1 : String = null 
    var result2 : String = null 

    getInfo1.onComplete{ 
     case Success(value) => result1 = value 
     case Failure(t) => println { 
     "An error has occured: " + t.getMessage 
     } 
    } 

    getInfo2.onComplete{ 
     case Success(value) => result2 = value 
     case Failure(t) => println { 
     "An error has occured: " + t.getMessage 
     } 
    } 
    /*I want to wait both Future function completed then 
     I can do some business logic */ 
    Breaks.breakable{ 
     while (true) { 
     if (result1 != null && result2 != null) 
      Breaks.break 
     } 
    } 

    println("----------------------") 
    println("result1:"+result1) 
    println("result2:"+result2) 
    println("----------------------") 
    } 

} 

После того как я компиляции и запуска этой программы, она выдавала ничего. просто подождите.

Run... 

Я ожидал, что я мог видеть выход:

Run... 
---------------------- 
result1:A 
result2:B 
---------------------- 

Итак, я добавил код в while цикле для отладки.

Breaks.breakable{ 
     while (true) { 
     println(result1) 
     println(result2) 
     if (result1 != null && result2 != null) 
      Breaks.break 
     } 
    } 

Тогда выход:

Run... 
A 
null 
A 
null 
A 
null 
A 
null 
A 
null 
(After 3 seconds...) 
A 
B 
---------------------- 
result1:A 
result2:B 
---------------------- 

Что происходит ?? Я просто добавлю два println, чтобы увидеть две переменные.

Почему программа может быть выполнена так, как я ожидал, когда я ее печатаю?

+2

Вы должны прочитать о параллельности и синхронизации. Такие вещи, как 'result2 = value', не так просты, как вы думаете, когда к ним обращаются несколько потоков. Это одна из главных причин, по которой они придумали scala, в первую очередь, где использование изменчивых переменных сильно обескуражено. Чтобы понять, почему ваш код не работает, попробуйте выполнить такие действия, как «volatile variable», «java synchronization», «jvm concurrency» и т. Д. Чтобы исправить это, попробуйте написать его по-другому, не используя изменяемые переменные. – Dima

+0

@Dima Из любопытства вы имеете в виду, что JIT просто оптимизирует его (кеш на уровне нити, который я предполагаю?), Поскольку он не ожидает, что эта переменная изменится? Так что, если я объявлю их '@ volatile', это может сработать? –

+0

Я не знаю, будет ли JIT оптимизировать его, но я не думаю, что это _that_ smart. Проблема в том, что значение переменной, вероятно, находится в регистре (или кэше процессора), и изменения не будут удалены в память до тех пор, пока не будет поврежден барьер памяти ('println' скорее всего создает один из них как побочный эффект, и поэтому вещи «таинственно» начинают работать, когда они добавляются). Да, в этом конкретном случае, объявляя, что vars 'volatile', вероятно, заставит его работать. Я бы не сказал, что это - путь к исправлению проблемы с этим кодом. – Dima

ответ

0

Future был создан, чтобы быть компонуемы, поэтому я рискну и предположим, что вы хотите что-то вроде этого:

// Important to initialize them outside of for comprehension 
val (f1, f2) = (getInfo1, getInfo2) 
val ab: Future[(String, String)] = 
    for { 
    r1 <- f1 
    r2 <- f2 
    } yield (r1, r2) // Do whatever you want to do with r1 and r2 

println(Await.result(ab, Duration(10000, MILLISECONDS))) 
+1

'val ab = getInfo1 zip getInfo2' :) – Dima

+0

@Dima В комментарии не распространяется« что угодно »:) Но спасибо, вы живете и учитесь. –

+2

ok, 'val ab = getInfo1 zip getInfo2 map {case (r1, r2) => any}': D – Dima