Scala: Как проверить параллелизм изменяемого файла - PullRequest
1 голос
/ 07 мая 2019

В Scala оба одновременных и не одновременных набора имеют абсолютно одинаковый тип:

// A regular Set in Scala, not concurrent.
val regularSet: mutable.Set[Int] = mutable.Set[Int]()

// A concurrent set. It has the same type as a regular set, but underneath, it is actually concurrent. In my opinion, this is a flaw in the type system for Scala collections
val concurrentSet: mutable.Set[Int] = java.util.concurrent.ConcurrentHashMap.newKeySet[Int]().asScala

Я хотел бы проверить, является ли набор одновременным или нет.

1 Ответ

1 голос
/ 07 мая 2019

Ответ

Вы можете проверить его опытным путем, создав множество потоков, которые пытаются добавить / удалить те же элементы из общего mutable.Set

import java.util.concurrent.{Callable, ExecutorService, Executors}
import scala.collection.mutable

def testSet(set: mutable.Set[Int]) {
  val e: ExecutorService = Executors.newFixedThreadPool(5)
  val tasks = for (i <- 0 to 50000) yield {
    e.submit(new Callable[Unit]() {
      override def call() {
        for (j <- 0 to 10) {
          set.add(i + j)

          // This will throw a Null Pointer Exception for the non-concurrent version
          // This is because it is trying to remove something that another thread already removed.
          set.remove(i + j) 
        }
      }
    })
  }
  for (result <- tasks) result.get()
  e.shutdown()
}

// Can't check the type! They have the same type.
val regularSet: mutable.Set[Int] = mutable.Set[Int]()
val concurrentSet: mutable.Set[Int] = java.util.concurrent.ConcurrentHashMap.newKeySet[Int]().asScala

testSet(concurrentSet) // Success!
testSet(regularSet) // FAILS! Almost always throws a NullPointerException on my system.

Ограничения

Выполнение теста потребует системных ресурсов, таких как потоки и время процессора. На самом деле нецелесообразно запускать это в рабочем режиме во время выполнения.

Это не дедуктивное доказательство . Существует небольшая вероятность того, что тест классифицирует не одновременный объект как параллельный, поскольку условие гонки является случайным. Однако выполнение теста дольше приведет к тому, что вероятность обнаружения несовпадающих объектов приблизится к достоверности.

Дополнительные комментарии

В идеале был бы способ использовать отражение и систему типов, чтобы увидеть, что лежал в основе объекта, и проверить, является ли он ConcurrentHashMap или нет (по моему мнению, это серьезный недостаток в Scala, поскольку некоторые функции, выполняющие несколько многопоточная задача не может эффективно предотвратить передачу функции вызывающей функции в непараллельный объект.

Но это, по крайней мере, дает эмпирический способ проверить это.

Подтверждения

Аналогичный вопрос был задан в Как проверить, что ConcurrentHashMap действительно поточно-ориентирован? . Я изменил его для работы с наборами:

Рекомендация

Я бы порекомендовал использовать concurrent.Map[T, Unit] вместо mutable.Set[T]. Причина в том, что вы сможете использовать систему типов, чтобы с 100% -ной уверенностью гарантировать, что объект, над которым работает ваша функция / класс, действительно параллелен.

Да, вы потеряете семантику Set, такую ​​как метод .add(setMember). Но вы получите безопасность.

Если вы настаиваете на использовании одновременного mutable.Set, рассмотрите возможность создания контейнера класса-обертки, чтобы вы могли предотвратить случайную инициализацию для не одновременного mutable.Set.

...