我有一个持续更新值的过程。我希望能够定期查询当前值的操作。在我的特定示例中,每个更新都可以被视为一种改进,并且该过程最终将收敛于最终的最佳答案,但我希望/需要访问中间结果。循环执行的速度和收敛所需的时间很重要。
举个例子,考虑一下这个循环:
var current = 0
while(current < 100){
current = current + 1
}
我希望能够在任何循环迭代中获得当前值。
与Actor的解决方案是:
class UpdatingActor extends Actor{
var current : Int = 0
def receive = {
case Update => {
current = current + 1
if (current < 100) self ! Update
}
case Query => sender ! current
}
}
你可以使用become或FSM摆脱var,但这个例子更清楚IMO。
或者,一个actor可以运行该操作并将每次循环迭代的更新结果发送给另一个actor,该actor唯一的职责是更新值并响应有关它的查询。我对Akka中的“代理人”了解不多,但这似乎是一个潜在的用例。
使用Scala执行此操作的更好/替代方法是什么?我不需要使用演员;这只是一个想到的解决方案。
答案 0 :(得分:1)
你的基于演员的解决方案还可以。
如果计算长时间阻止演员并且您想确保始终获得中间结果,那么在每次更改后将中间结果发送到“结果提供者”演员也是一个好主意。另一种选择是使实际的计算器演员成为收集最佳结果的演员的孩子。这样,事物就会从外部充当单个actor,并且你有一个actor,它具有与执行计算的actor分开的状态(当前最佳结果),这可能会失败。
agent在非常低级 @ volatile / AtomicInteger 方法与Actor之间有一定的解决方案。代理只能通过对其进行转换来修改(并且存在转换队列),但具有始终可以访问的当前状态。虽然它不是位置透明的。如果你需要的话,请继续使用演员的方法。
以下是使用代理解决此问题的方法。你有一个线程执行长时间运行的计算(由Thread.sleep模拟)和另一个线程,它只是以固定间隔打印出最佳当前结果(也由Thread.sleep模拟)。
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent._
import akka.agent.Agent
object Main extends App {
val agent = Agent(0)
def computation() : Unit = {
for(i<-0 until 100) {
agent.send { current =>
Thread.sleep(1000) // to simulate a long-running computation
current + 1
}
}
}
def watch() : Unit = {
while(true) {
println("Current value is " + agent.get)
Thread.sleep(1000)
}
}
global.execute(new Runnable {
def run() = computation
})
watch()
}
但总而言之,我认为基于演员的解决方案会更优越。例如,您可以在与结果跟踪不同的机器上进行计算。
答案 1 :(得分:0)
问题的范围有点宽,但我会尝试:)
首先,你的例子非常好,我没有看到摆脱var
的意义。这就是演员的目的:保护可变状态。
其次,根据你描述的内容,你根本不需要演员。
class UpdatingActor {
private var current = 0
def startCrazyJob() {
while(current < 100){
current = current + 1
}
}
def soWhatsGoingOn: Int = current
}
您只需要一个帖子就可以拨打startCrazyJob
,第二个线程会定期拨打soWhatsGoingOn
。