2015-11-12 3 views
2

Мне нужно кэшировать что-то в Scala в многопоточной среде.Является экземпляром класса, который обновляет только один безопасный поток в Scala?

Чтение на scalaz-х Memo я нашел следующий комментарий в the code для неизменной хэш карты памятки:

В этом документе используется один варом, это потокобезопасное.

код выглядит следующим образом:

def immutableMapMemo[K, V](m: Map[K, V]): Memo[K, V] = { 
    var a = m 

    memo[K, V](f => 
     k => { 
     a get k getOrElse { 
      val v = f(k) 
      a = a updated (k, v) 
      v 
     } 
     }) 
    } 

Сказать, что это поточно противоречит тому, что я прочитал и узнал, до сих пор о безопасности потоков на JVM-платформы; Исходные обновления могут быть атомарными, но, как я понял, компилятор может попытаться выполнить определенные оптимизации, которые нарушают связь между событиями, если у вас нет барьера памяти. См., Например, this post и this.

Но я уверен, что люди из скасаза довольно умны. Может быть, есть что-то особенное в области a.

Является ли это то, что комментарий претендует на истину, и если да, то почему?

+0

Что на самом деле идет против потоковой безопасности в вашем опыте? 'var' update - это ссылочное задание, поэтому оно является атомарным. Все остальные операции не мутируют объекты, поэтому в любое время в 'a' есть некоторая согласованная версия' Map [k, V] ' – Odomontois

+0

. Я уточню свой вопрос, чтобы уточнить. –

ответ

5

Прежде всего, поскольку var не помечен @volatile, вы можете увидеть разные версии a в различных потоках. Таким образом, вы можете делать расчет несколько раз для разных потоков. Этот вид побеждает цель memoization, но кроме этого он не наносит никакого вреда, при условии, что функция, memoized, не имеет побочных эффектов.

Кроме того, на архитектуре x86 вы почти всегда увидите изменения, выполненные в одном потоке на всех других потоках.

Что касается внутренней согласованности карты: Насколько я знаю, в этом случае не представляется возможным наблюдать карту, хранящуюся в в неопределенном состоянии, потому что карта не только Наблюдаемая неизменны, но все версии Карта (Map1, Map2, Map3, Map4, HashMap1, HashTrieMap, HashMapCollision1, EmptyMap) имеет только конечные поля и поэтому безопасна в соответствии с моделью java-памяти. Однако, полагаясь на это, очень хрупко.

Например, если a будет содержать список или Vector, вы бы иметь возможность наблюдать его в неустойчивом состоянии, когда быстро обновлять его из разных потоков. Причиной этого является то, что эти структуры данных являются неизменно неизменными, но do использует изменчивое состояние внутри для оптимизации производительности.

Так нижняя линия: не полагаться на это для запоминания в многопоточном контексте.

См this thread на лестницу пользователя для обсуждения очень похожих проблем

См this thread, почему даже основные Наблюдаемый неизменные структуры данных, таких как List и Vector можно наблюдать в неустойчивом состоянии, если не с помощью безопасной публикации через @volatile или другой безопасный механизм, такой как актеры.