2011-07-29 19 views
19

不変のスカラコレクションを安全に更新するための「簡単な」方法があるかどうかは疑問でした。次のコードを検討してください:Scalaコレクションをスレッドセーフに更新する

class a { 
    private var x = Map[Int,Int]() 

    def update(p:(Int,Int)) { x = x + (p) } 
} 

このコードはスレッドセーフではありませんが、正しいですか?これは、updateメソッドを呼び出す2つのスレッドがあり、xが{1 => 2}のマップであり、スレッドAがupdate((3,4))を呼び出すとx +(p)を実行するだけの場合、コードの一部。その後、再スケジューリングが発生し、スレッドBはupdate((13,37))を呼び出し、変数xを正常に更新します。スレッドAは継続して終了します。

これで、値xは{1 => 2、3 => 4}のマップと等しくなりますか?希望の{1 => 2,3、> 4,13 => 37}の代わりに。それを修正する簡単な方法はありますか?

Btw、私はAkka STMのようなソリューションがあることを知っていますが、私は必要がない限り、それらを使用したくないと思います。

ありがとうございました!

編集:また、私はロックなしの解決策を好むでしょう。 Eeeew :)

+1

使用アッカSTM http://akka.io/docs/akka/1.1.3/ scala/stm.html – Phil

+0

+! Eeeewのために! :D – Daniel

答えて

18

あなたのケースでは、Maurícioが書いたように、コレクションは不変であるため、すでにスレッドセーフです。唯一の問題は、アトミック操作ではないvarを再割り当てすることです。この特定の問題については、java.util.concurrent.atomicの素敵なクラス、つまりAtomicReferenceを使うのが最も簡単な方法です。

import java.util.concurrent.atomic.AtomicReference 

class a { 
    private val x = new AtomicReference(Map[Int,Int]()) 

    def update(p:(Int,Int)) { 
    while (true) { 
     val oldMap = x.get // get old value 
     val newMap = oldMap + p // update 
     if (x.compareAndSet(oldMap, newMap)) 
     return // exit if update was successful, else repeat 
    } 
    } 
} 
+1

は、このブロック全体を同期する方が簡単です。オーバーロックされた操作はメモリバリアを作成します。 – tuxSlayer

+1

同期は簡単かもしれませんが、同期が必要なく安全であり、マップが多くのスレッドによって頻繁に更新される場合を除いて、より効率的です。 –

+1

実際には、varを使用してホットスワップ操作を同期させることは、リソースの消費量が少なくなります。これを見てください:[リンク](http://twitter.github.io/scala_school/concurrency.html) - 「これは何でもかかりますか?セクション –

3

コレクション自体は、共有可能な可変状態を持たないため、スレッドセーフですが、コードが変更されていないため、ロックされていない状態でこれを修正する方法はありません。あなたの最善の選択肢は、メソッド自体を同期としてマークすることです。

他の解決策は、おそらくjava.util.concurrent.ConcurrentMapの可変同時マップを使用することです。

0

Re。ジャン=フィリップ・ペレットの答え:あなたは、再利用可能なこの少しを行うことができます。

def compareAndSetSync[T](ref: AtomicReference[T])(logic: (T => T)) { 
    while(true) { 
    val snapshot = ref.get 
    val update = logic(snapshot) 
    if (ref.compareAndSet(snapshot, update)) return 
    } 
} 

def compareSync[T,V](ref: AtomicReference[T])(logic: (T => V)): V = { 
    var continue = true 
    var snapshot = ref.get 
    var result = logic(snapshot) 
    while (snapshot != ref.get) { 
    snapshot = ref.get 
    result = logic(snapshot) 
    } 
    result 
} 
+0

同様に、http://blog.scala4java.com/2012/03/atomic-update-of-atomicreference.html かなり一般的な使用例のようです。 – experquisite

4
  1. ボックスのうち
  2. アトミック
  3. ロックフリー
  4. O(1)

チェックこのアウト:http://www.scala-lang.org/api/2.11.4/index.html#scala.collection.concurrent.TrieMap

+0

なぜhttp://www.scala-lang.org/api/2.11.4/index.html#scala.collection.concurrent.Map? – matanster

+0

@mattそれはちょうどインターフェイスのような特性ですか? –

+0

はい、あなたは正しいです、AFAIK TrieMapは、唯一のscala提供の実装です。 – matanster

関連する問題