Multithreading 多线程记忆的函数参数同步
我的核心问题是:如何结合对象实例和方法参数在方法中实现同步 以下是我的详细情况。我使用以下代码实现备忘录,改编自: 在我的项目中,我正在记录Multithreading 多线程记忆的函数参数同步,multithreading,scala,asynchronous,memoization,thread-synchronization,Multithreading,Scala,Asynchronous,Memoization,Thread Synchronization,我的核心问题是:如何结合对象实例和方法参数在方法中实现同步 以下是我的详细情况。我使用以下代码实现备忘录,改编自: 在我的项目中,我正在记录Futures以消除异步API调用中的重复数据。当使用for…yield映射使用标准ExcecutionContext创建的结果期货时,这种方法效果很好,但是当我升级到时,为了更好地处理这些期货。然而,我意识到库使用的多线程允许多个线程进入apply,从而破坏了记忆,因为async阻止了所有并行执行,在缓存可以用新的未来更新之前进入“orElse”thunk
Future
s以消除异步API调用中的重复数据。当使用for…yield
映射使用标准ExcecutionContext
创建的结果期货时,这种方法效果很好,但是当我升级到时,为了更好地处理这些期货。然而,我意识到库使用的多线程允许多个线程进入apply
,从而破坏了记忆,因为async
阻止了所有并行执行,在缓存
可以用新的未来
更新之前进入“orElse”thunk
为了解决这个问题,我将主应用函数放在this.synchronized
块中:
def apply(x: T): R = this.synchronized {
cache.getOrElse(x, {
val y = f(x)
cache += ((x, y))
y
})
}
这恢复了记忆化的行为。缺点是,这将阻止使用不同参数的调用,至少在创建未来之前是如此。我想知道是否有一种方法可以在memorized
实例和x
参数的值与apply
的组合上设置更细粒度的同步。这样,只会阻止重复数据消除的呼叫
作为旁注,我不确定这是否真的是性能关键,因为一旦创建并返回了未来的,同步块就会释放(我想是吧?)。但如果有任何我没有想到的问题,我也想知道。Akka参与者与未来相结合,提供了一种强大的方式,可以在不阻塞的情况下结束可变状态。下面是一个简单的示例,说明如何使用参与者进行记忆:
import akka.actor._
import akka.util.Timeout
import akka.pattern.ask
import scala.concurrent._
import scala.concurrent.duration._
class Memoize(system: ActorSystem) {
class CacheActor(f: Any => Future[Any]) extends Actor {
private[this] val cache = scala.collection.mutable.Map.empty[Any, Future[Any]]
def receive = {
case x => sender ! cache.getOrElseUpdate(x, f(x))
}
}
def apply[K, V](f: K => Future[V]): K => Future[V] = {
val fCast = f.asInstanceOf[Any => Future[Any]]
val actorRef = system.actorOf(Props(new CacheActor(fCast)))
implicit val timeout = Timeout(5.seconds)
import system.dispatcher
x => actorRef.ask(x).asInstanceOf[Future[Future[V]]].flatMap(identity)
}
}
我们可以像这样使用它:
val system = ActorSystem()
val memoize = new Memoize(system)
val f = memoize { x: Int =>
println("Computing for " + x)
scala.concurrent.Future.successful {
Thread.sleep(1000)
x + 1
}
}
import system.dispatcher
f(5).foreach(println)
f(5).foreach(println)
“计算5”将只打印一次,而“6”将打印两次
有一些看起来吓人的调用实例,但它是完全类型安全的。您考虑过使用actor吗?然后,客户端代码将永远不会阻塞响应,只有一个线程将触及缓存
。我没有想到这一点,但根据我所知的一点,这听起来更像是一个惯用的解决方案。你能解释更多或者提供一个链接来说明这是如何从控制器代码中使用API进行web响应的吗?好吧,据我所知,多线程对建立记忆没有多大帮助:升级,因为我认为这是一个很好的工具。我仍在考虑采用它是否有意义,因为这是一个需要承担的额外样板。这也将缓存失败的未来。
val system = ActorSystem()
val memoize = new Memoize(system)
val f = memoize { x: Int =>
println("Computing for " + x)
scala.concurrent.Future.successful {
Thread.sleep(1000)
x + 1
}
}
import system.dispatcher
f(5).foreach(println)
f(5).foreach(println)