2

我有一个抽象的特征,对计算有一些要求,然后是对这些计算结果的一些函数。我想保持这个特性简单,以便于理解和测试。

trait Calculator {
  def hardToCalculate1: Int
  def hardToCalculate2: Int
  def hardToCalculate3: Int

  def result1 = hardToCalculate1 + hardToCalculate2
  def result2 = hardToCalculate2 + hardToCalculate3
  def result3 = hardToCalculate1 + hardToCalculate3
}

当我实例化 aCalculator时,我将使用 Futures 来计算这些hardToCalculate值。假设它们看起来像这样:

def f1 = future {
    println("calculating 1")
    1
}
def f2 = future {
    println("calculating 2")
    2
}
def f3 = future {
    println("calculating 3")
    3
}

所以,我可以构造一个Future[Calculator]这样的:

val myCalc = for {
  m1 <- f1
  m2 <- f2
  m3 <- f3
} yield new Calculator {
  lazy val hardToCalculate1 = m1
  lazy val hardToCalculate2 = m2
  lazy val hardToCalculate3 = m3
}

然后,我可能会myCalc这样使用:

myCalc onComplete {
  case Success(calc) => println("Result: " + calc.result1)
}

但是当我这样做时,我得到了这个:

calculating 1
calculating 2
calculating 3
Result: 3

如果我正在做的计算确实需要它们,我只想执行这些期货。尽管我用 s 声明了hardToCalculates lazy val,但所有三个都是在Future[Calculator].onComplete执行时计算的。

一种方法是这样的:

val calculator = new Calculator {
    lazy val hardToCalculate1 = Await.result(f1, 10 seconds)
    lazy val hardToCalculate2 = Await.result(f2, 10 seconds)
    lazy val hardToCalculate3 = Await.result(f3, 10 seconds)
}
println("result: " + calculator.result1)

这产生了我想要的:

calculating 1
calculating 2
result: 3

但现在我有所有的Await障碍。我真正想要的是以Future[Calculator]一种懒惰的方式执行期货。如果不将 Futures 引入我的Calculator特征,这可能吗?关于如何在这里获得我想要的东西的任何其他建议?

上面所有代码的要点都在这里。

4

2 回答 2

2

如果您创建一个Future(使用scala.concurrent.future),无论您做什么,它都会被计算出来。所以你需要一个完全不同的策略。

此外,您的界面甚至不允许远程确定您将实际使用哪些数据。应该如何计算myCalc知道在onComplete你只会使用result1

你可以:

  • 仅使用惰性 val:

    val calculator = new Calculator {
      lazy val hardToCalculate1 = {
        println("calculating 1")
        1
      }
      // ...
    }
    

    优点:简单
    缺点:非异步

  • 封装Future以允许请求计算:

    class ReqFuture[T](body: () => T) {
      lazy val fut = future { body() }
    }
    

    但是现在你仍然有一个问题myCalc会请求并等待所有这些。所以你必须ReqFutures介绍Calculator

    trait Calculator {
        def hardToCalculate1: ReqFuture[Int]
        // ...
        def result1 = for {
          h1 <- hardToCalculate1.fut
          h2 <- hardToCalculate2.fut
        } yield h1 + h2
    }
    

    临:当你打电话时result1,只计算你需要的(但仍然只计算一次)。
    缺点:result1Future[Int]现在。所以Futures已经完全渗透到你的Calculator.

如果您无法影响Calculator(我怀疑)并且无法更改代码,result1,2,3那么不幸的是,据我所知,您无法使执行变得懒惰和异步。

于 2013-07-15T23:06:56.157 回答
0

我想试试新的Async API,这是一个很好的测试。事实上,在 Async Github 主页上有一个与您的用例非常接近的示例。

Async 是一个SIP,并且很可能在某个时候成为标准 Scala 的一部分。

除了使用await这里的想法之外,您还有一个add()在幕后使用异步逻辑的抽象方法。这样它就对Calculator开发人员隐藏了。

就我个人而言,我也会添加 API 的异步版本,以便Futures 可以从 s 中传递出来Calculator以与其他Futures 组合。

trait Calculator {
  def hardToCalculate1: Int
  def hardToCalculate2: Int
  def hardToCalculate3: Int

  def add(a: => Int, b: => Int): Int

  def result1 = add(hardToCalculate1, hardToCalculate2)
  def result2 = add(hardToCalculate2, hardToCalculate3)
  def result3 = add(hardToCalculate1, hardToCalculate3)
}

object So17677728 extends App with Calculator {

  override def add(a: => Int, b: => Int): Int = {
    val start = System.currentTimeMillis

    val res = Await.result(asyncAdd(a, b), 2000 millis)

    val end = System.currentTimeMillis

    println(s"Total time: ${end - start} ms")

    res
  }

  def asyncAdd(a: => Int, b: => Int): Future[Int] = {
    async {
      val fa = Future(a)
      val fb = Future(b)
      await(fa) + await(fb)
    }
  }

  val random = Random
  val maxSleep = 1000

  def hardToCalculate1: Int = htc(1)
  def hardToCalculate2: Int = htc(2)
  def hardToCalculate3: Int = htc(3)

  def htc(n: Int) = {
    val sleepMs = random.nextInt(1000)
    println(s"$n sleeping for $sleepMs ms")
    Thread.sleep(sleepMs)
    println(s"$n done sleeping")
    n
  }

  println(s"result1: $result1\n")
  println(s"result2: $result2\n")
  println(s"result3: $result3\n")
}

输出

1 sleeping for 438 ms
2 sleeping for 244 ms
2 done sleeping
1 done sleeping
Total time: 497 ms
result1: 3

3 sleeping for 793 ms
2 sleeping for 842 ms
3 done sleeping
2 done sleeping
Total time: 844 ms
result2: 5

3 sleeping for 895 ms
1 sleeping for 212 ms
1 done sleeping
3 done sleeping
Total time: 896 ms
result3: 4

或者,add您可以Future使用 s 和 for 理解而不是async/ await

于 2013-07-16T13:22:55.100 回答