Scala 即使是';它没有被使用
我有以下设置原子变量的代码(java.util.concurrent.Atomic和monix.execution.Atomic的行为相同:Scala 即使是';它没有被使用,scala,java.util.concurrent,monix,Scala,Java.util.concurrent,Monix,我有以下设置原子变量的代码(java.util.concurrent.Atomic和monix.execution.Atomic的行为相同: class Foo { val s = AtomicAny(null: String) def foo() = { println("called") /* Side Effects */ "foo" } def get(): String = { s.compareAndSet(null, foo(
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,并给我错误,因为它试图创建重复的actor)
如何确保第二个参数在实际使用之前不会被计算?(最好不要使用synchronized)
我需要将隐式参数传递给foo,这样lazy 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,您就不需要使用低级原语进行自己的线程同步。actor模型的全部要点是将线程之间的交互限制为仅传递异步消息。这提供了您需要的所有线程同步,并保证了actor处理单个混乱以单线程方式一次老化
您绝对不应该拥有一个由多个线程同时访问的函数来创建一个单一参与者。只需在您拥有所需信息时创建参与者,并使用依赖项注入或消息将ActorRef
传递给任何其他需要它的参与者。或者在开始时创建参与者并初始化它当第一条消息到达时(使用context.been
管理参与者状态)
原始答案 最简单的解决方案就是使用
lazy val
来保存foo
的实例:
class Foo {
lazy val foo = {
println("called")
/* Side Effects */
"foo"
}
}
这将在第一次使用时创建foo
,然后返回相同的值
如果由于某种原因无法执行此操作,请使用初始化为0
的AtomicInteger
,然后调用incrementAndGet
。如果返回1
,则这是此代码的第一次执行,您可以调用foo
说明:
原子操作,如compareAndSet
需要CPU指令集的支持,现代处理器为此类操作提供单个原子指令。在某些情况下(例如,缓存线由该处理器独占),操作可能非常快。其他情况下(例如,缓存线也位于另一个处理器的缓存中)该操作可能会显著减慢,并可能影响其他线程
结果是,在执行原子指令之前,CPU必须持有新值。因此,在知道是否需要该值之前,必须先计算该值。当它最初为
null
,但调用foo
后不再为null
时,您希望它如何运行?您不需要锁吗如果你不想发生这种情况?@snak我不确定我是否在跟踪……我想可能对我的问题有误解。请看我刚刚更新。好吧,原子变量支持同时比较和交换值,而不使用锁(可能使用单CPU指令).但要做到这一点,它需要在调用compareAndSet
之前知道要交换的值。这就是为什么它的参数不能是惰性的,并且在调用自己的函数时需要一个显式锁来锁定它。lazy val
在我的情况下不起作用,因为副作用不应该在类初始化时发生,一个d我需要将隐式参数传递给foo,这是lazy val无法做到的。@texasbruce副作用不是在类初始化时发生的,而是在第一次访问foo
时发生的。您可以从接受implicit
参数的函数中分配foo
。隐式参数由调用