问题描述
假设我想在Scala中使用可变映射来跟踪我看到一些字符串的次数。在单线程上下文中,这很容易:
Suppose I want to use a mutable map in Scala to keep track of the number of times I've seen some strings. In a single-threaded context, this is easy:
import scala.collection.mutable.{ Map => MMap }
class Counter {
val counts = MMap.empty[String, Int].withDefaultValue(0)
def add(s: String): Unit = counts(s) += 1
}
不幸的是,这不是线程安全,因为 get
和更新
不会原子发生。
Unfortunately this isn't thread-safe, since the get
and the update
don't happen atomically.
添加到可变地图API,但不是我需要的,其将看起来像这样:
Concurrent maps add a few atomic operations to the mutable map API, but not the one I need, which would look something like this:
def replace(k: A, f: B => B): Option[B]
我知道我可以使用的:
I know I can use ScalaSTM's TMap
:
import scala.concurrent.stm._
class Counter {
val counts = TMap.empty[String, Int]
def add(s: String): Unit = atomic { implicit txn =>
counts(s) = counts.get(s).getOrElse(0) + 1
}
}
但是(现在),这仍然是一个额外的依赖。其他选项将包括actors(另一个依赖关系),同步(可能效率较低)或Java的()。
But (for now) that's still an extra dependency. Other options would include actors (another dependency), synchronization (potentially less efficient), or Java's atomic references (less idiomatic).
一般来说,我会避免在Scala中的可变地图,但我偶尔需要这种东西,最近我使用的STM方法(而不是只是越过我的手指,希望我don
In general I'd avoid mutable maps in Scala, but I've occasionally needed this kind of thing, and most recently I've used the STM approach (instead of just crossing my fingers and hoping I don't get bitten by the naïve solution).
我知道这里有一些权衡(额外的依赖关系与性能对比清晰度等)但在Scala 2.10中有什么像正确的答案这个问题吗?
I know there are a number of trade-offs here (extra dependencies vs. performance vs. clarity, etc.), but is there anything like a "right" answer to this problem in Scala 2.10?
推荐答案
这个怎么样?假设你现在不需要一个一般的替换
方法。
How about this one? Assuming you don't really need a general replace
method right now, just a counter.
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger
object CountedMap {
private val counts = new ConcurrentHashMap[String, AtomicInteger]
def add(key: String): Int = {
val zero = new AtomicInteger(0)
val value = Option(counts.putIfAbsent(key, zero)).getOrElse(zero)
value.incrementAndGet
}
}
您在整个地图上获得比同步更好的性能,您还可以获得原子增量。
You get better performance than synchronizing on the whole map, and you also get atomic increments.
这篇关于线程安全地变换可变映射中的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!