【问题标题】:How do I change this implementation to use single synchronization block instead of multiple?如何更改此实现以使用单个同步块而不是多个同步块?
【发布时间】:2017-10-31 21:29:30
【问题描述】:

我的系统中有事件和一个根据事件和最后计算值计算值的函数:

当我调用这个函数时,我想要这种行为:

  • 如果有新的事件,它应该使用事件和最后记忆的值计算新值,并记忆结果
  • 如果没有新事件,它应该返回最后一个被记忆的值

这是我想出的实现:

<!-- language: lang-scala -->
class EventBasedMemo[Value, Event](initialValue: => Value,
                                    buildValue: (Value, Event) => Value,
                                    nextEvent: Option[Event] = None) {

  lazy val memoValue: Value = 
    nextEvent.fold(initialValue)(event => buildValue(initialValue, event))

  def update(event: Event): EventBasedMemo[Value, Event] =
    new EventBasedMemo(memoValue, buildValue, Option(event))

}

type Value = Int
type Event = String
var callsCount = 0

var memo = new EventBasedMemo[Value, Event](0, (value, event) => {
  callsCount += 1
  println(s"$callsCount $event")
  value + 1
})

memo = memo.update("A")
memo = memo.update("B")
memo = memo.update("C")

memo.memoValue //  1 A 2 B 3 C
memo.memoValue

memo = memo.update("D")

memo.memoValue // 4 D

这很好用。但问题是lazy val 在后台使用同步,因此计算时会发生多个嵌套同步,这很糟糕。

如何更改它以使其仅使用一次同步?

我希望解决方案是不可变的。

【问题讨论】:

    标签: scala synchronization lazy-evaluation memoization


    【解决方案1】:

    使用手动记忆可以避免同步阻塞。但在多线程场景中,缓存可能会重新计算多次。仅当计算没有副作用(如示例中没有打印语句)时,重新计算它是安全的。

    class EventBasedMemo[Value, Event](initialValue: => Value,
                                       buildValue: (Value, Event) => Value,
                                       nextEvent: Option[Event] = None) {
    
      var cache: Option[Value] = None
    
      def memoValue(): Value = {
        cache match {
          case None =>
            cache = Some(nextEvent.fold(initialValue)(event => buildValue(initialValue, event)))
            cache.get
          case Some(value) => value
        }
      }
    
      def update(event: Event): EventBasedMemo[Value, Event] =
        new EventBasedMemo(memoValue, buildValue, Option(event))
    
    }
    

    如果你真的想拥有快速同步的惰性值,那么看看像ReentrantReadWriteLock 这样的读/写锁。下一段代码只是演示,不确定在多线程环境下能否正常运行。

    import java.util.concurrent.locks.ReentrantReadWriteLock
    
    class EventBasedMemo[Value, Event](initialValue: => Value,
                                       buildValue: (Value, Event) => Value,
                                       nextEvent: Option[Event] = None,
                                       lock:ReentrantReadWriteLock = new ReentrantReadWriteLock()) {
    
      var cache: Option[Value] = None
    
      def memoValue(): Value = {
        lock.readLock().lock()
        val result = cache match {
          case None =>
            lock.readLock().unlock()
            lock.writeLock().lock()
            cache = Some(nextEvent.fold(initialValue)(event => buildValue(initialValue, event)))
            lock.writeLock().unlock()
            lock.readLock().lock()
            cache.get
          case Some(value) => value
        }
        lock.readLock().unlock()
        result
      }
    
      def update(event: Event): EventBasedMemo[Value, Event] =
        new EventBasedMemo(memoValue, buildValue, Option(event), lock)
    
    }
    

    【讨论】:

      猜你喜欢
      • 2010-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-13
      • 1970-01-01
      相关资源
      最近更新 更多