原子比较和设置参数即使未使用也会被计算



i具有以下代码来设置原子变量(java.util.concurrent.atomicmonix.execution.atomic的行为相同:

class Foo {
  val s = AtomicAny(null: String)
  def foo() = {
    println("called")
    /* Side Effects */ 
    "foo" 
  }
  def get(): String = {
    s.compareAndSet(null, foo())
    s.get
  }
}

val f = new Foo
f.get //Foo.s set from null to foo, print called
f.get //Foo.s not updated, but still print called

第二次比较时,它没有更新值,但仍调用了foo。这会导致问题,因为foo具有副作用(在我的真实代码中,它创建了Akka Actor并给我错误,因为它试图创建重复的演员(。

除非实际使用,否则如何确保未评估第二个参数?(最好不使用同步(

我需要将隐式参数传递给foo,以便懒惰的val行不通。例如

  lazy val s = get() //Error cannot provide implicit parameter
  def foo()(implicit context: Context) = {
    println("called")
    /* Side Effects */ 
    "foo" 
  }
  def get()(implicit context: Context): String = {
    s.compareAndSet(null, foo())
    s.get
  }

更新答案

快速答案是将此代码放入演员中,然后您不必担心同步。

如果您使用的是Akka Actors,则无需使用低级原始词进行自己的线程同步。演员模型的全部要点是将线程之间的相互作用限制为传递异步消息。这提供了您需要的所有线程同步,并确保演员一次以单线读取方式处理一条消息。

您绝对不应该拥有由创建单身演员的多个线程同时访问的函数。只需在拥有所需的信息时创建演员,然后将ActorRef传递给其他需要使用依赖注入或消息的参与者。或在开始时创建演员并在第一个消息到达时初始化(使用context.become来管理Actor State(。


原始答案

最简单的解决方案只是使用lazy val保存您的foo实例:

class Foo {
  lazy val foo = {
    println("called")
   /* Side Effects */ 
   "foo" 
  }
}

这将在第一次使用时创建foo,然后将返回相同的值。

如果由于某种原因无法实现这一目标,请使用初始化为0AtomicInteger,然后致电incrementAndGet。如果返回1,则是第一个通过此代码,您可以致电foo

说明:

诸如compareAndSet之类的原子操作需要CPU指令集的支持,现代处理器具有单一的原子指令。在某些情况下(例如,缓存线仅由此处理器持有(,操作可能非常快。其他情况(例如,在另一个处理器的高速缓存中也在缓存线(该操作可能会较慢,并且可能会影响其他线程。

结果是,在执行原子指令之前,CPU必须持有新值。因此,必须在知道是否需要之前计算该值。

相关内容

  • 没有找到相关文章

最新更新