ソリューションの問題は、アクターを呼び出すとnew HeavyClass()
、その計算が処理されるまでブロックされることです。Future または別の Actor でそれを行うと、それを回避できます。これを行う1つの方法は次のとおりです。
import akka.pattern.pipe
...
class HeavyClassProvider extends Actor {
// start off async computation during init:
var hc: Future[HeavyClass] = Future(new HeavyClass)
override def receive = {
case "REQUEST" =>
// send result to requester when it's complete or
// immediately if its already complete:
hc pipeTo sender
// start a new computation and send to self:
Future(new HeavyClass) pipeTo self
case result: HeavyClass => // new result is ready
hc = Future.successful(result) // update with newly computed result
case Status.Failure(f) => // computation failed
hc = Future.failed[HeavyClass](f)
// maybe request a recomputation again
}
}
(私はそれをコンパイルしませんでした)
私の最初のソリューションの特徴の 1 つは、同時に計算される Future の数を制限しないことです。複数のリクエストを受け取った場合、複数の先物が計算されますが、このアクタには競合状態はありません。これを制限するには、アクタに Boolean フラグを導入して、既に何かを計算しているかどうかを通知します。また、これらすべてvar
の は動作に置き換えることができますbecome/unbecome
。
複数のリクエストが与えられた単一の同時 Future 計算の例:
import akka.pattern.pipe
...
class HeavyClassProvider extends Actor {
// start off async computation during init:
var hc: Future[HeavyClass] = Future(new HeavyClass) pipeTo self
var computing: Boolean = true
override def receive = {
case "REQUEST" =>
// send result to requester when it's complete or
// immediately if its already complete:
hc pipeTo sender
// start a new computation and send to self:
if(! computing)
Future(new HeavyClass) pipeTo self
case result: HeavyClass => // new result is ready
hc = Future.successful(result) // update with newly computed result
computing = false
case Status.Failure(f) => // computation failed
hc = Future.failed[HeavyClass](f)
computing = false
// maybe request a recomputation again
}
}
編集:コメントで要件についてさらに議論した後、ここでは、各リクエストで新しいオブジェクトを非ブロッキング方式で送信者/クライアントに送信する別の実装を示します。
import akka.pattern.pipe
...
class HeavyClassProvider extends Actor {
override def receive = {
case "REQUEST" =>
Future(new HeavyClass) pipeTo sender
}
}
そして、次のように単純化できます。
object SomeFactoryObject {
def computeLongOp: Future[HeavyClass] = Future(new HeavyClass)
}
この場合、アクターは必要ありません。このような場合に同期メカニズムおよびノンブロッキング計算としてアクタを使用する目的は、そのアクタが結果をキャッシュし、単なる よりも複雑なロジックで非同期計算を提供することです。Future
それ以外の場合Future
は十分です。