线程安全地转换可变地图中的值 [英] Thread-safely transforming a value in a mutable map

查看:171
本文介绍了线程安全地转换可变地图中的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我想在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]

我知道我可以使用 ScalaSTM TMap

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆