-3

What I want to do is to use Future to open a thread to handle an async task that could be called frequently.

But in the async task. I also call two Future function to get the information from different data source.

I write a program to simulate this situation.

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("----------------------")
  }

}

After I compiled and executed this program, it output nothing. just wait.

Run...

I expected that I could see output :

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

So, I added some code in while loop for debugging .

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

Then it output :

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

What's going on?? I just add two println to see the two variables.

Why the program could be executed as I expected when I just print it?

cchantep
  • 9,118
  • 3
  • 30
  • 41
  • 2
    You need to read up on concurrency and synchronization. Things like `result2 = value` are not as easy to do as you think at all when multiple threads are accessing it. That's one of the main reasons they invented scala in the first place, where the use of mutable variables is strongly discouraged. To understand why your code does not work, try googling things like "volatile variable", "java synchronization", "jvm concurrency" etc. To fix it, try writing it in a different way, without using mutable variables. – Dima Jun 21 '16 at 17:30
  • @Dima Out of curiosity, you mean JIT will simply optimize it out (cache on the thread level I presume?) as it doesn't expect this variable to change? So if I declare them `@volatile` it may work? – Victor Moroz Jun 21 '16 at 17:41
  • I don't know if JIT will optimize it out, but I don't think it is _that_ smart. The problem is that the variable value is, probably sitting in a register (or CPU cache), and changes will not be flushed to memory until a memory barrier is hit (`println` call likely creates one as a side effect, and that's why things "mysteriously" start working when it is added). Yes, in this particular case, declaring the vars `volatile` will, probably, make it work. I would not say, that it is the _right_ way to fix the problem with this code though. – Dima Jun 21 '16 at 17:47
  • These variables are not "optimized out", there is something going on with breakable `while(true)` that is blocking. That's as far as I could conclude. – pedrofurla Jun 21 '16 at 18:27
  • Btw, another thing catching my eyes is the `getInfo`s are methods, so, the first time they appear in the asyncTask are different futures than the ones where the `onComplete` is attached. – pedrofurla Jun 21 '16 at 19:14

1 Answers1

0

Future was created to be composable, so I will take a risk and assume that you wanted something like this:

// 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)))
Victor Moroz
  • 9,167
  • 1
  • 19
  • 23