线程安全地转换可变地图中的值 [英] Thread-safely transforming a value in a mutable map
问题描述
假设我想在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方法(而不是只穿过我的手指,希望我不会被天真的解决方案所困扰)。
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.
这篇关于线程安全地转换可变地图中的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!