Является ли этот параллельный массив scala потокобезопасным?

Я хочу использовать параллельные массивы для задачи, и прежде чем я начну с кодирования, мне было бы интересно узнать, является ли этот небольшой фрагмент кода потокобезопасным:

import collection.mutable._

var listBuffer = ListBuffer[String]("one","two","three","four","five","six","seven","eight","nine")
var jSyncList  = java.util.Collections.synchronizedList(new java.util.ArrayList[String]())
listBuffer.par.foreach { e =>
    println("processed :"+e)
    // using sleep here to simulate a random delay
    Thread.sleep((scala.math.random * 1000).toLong)
    jSyncList.add(e)
}
jSyncList.toArray.foreach(println)

Существуют ли более эффективные способы обработки чего-либо с параллельными коллекциями и сбора результатов в других местах?

5 ответов

Решение

Код, который вы разместили, совершенно безопасен; Я не уверен насчет предпосылки: зачем вам нужно накапливать результаты параллельного сбора в непараллельный? Одним из достоинств параллельных коллекций является то, что они похожи на другие коллекции.

Я думаю, что параллельные коллекции также обеспечат seq способ переключения на последовательные. Так что вы, вероятно, должны использовать это!

Чтобы этот шаблон был безопасным:

listBuffer.par.foreach { e => f(e) }

f должен быть в состоянии одновременно работать безопасным способом. Я думаю, что применяются те же правила, которые необходимы для безопасной многопоточности (доступ к общему состоянию должен быть поточно-безопасным, порядок f призывает к разным e не будет детерминированным, и вы можете столкнуться с тупиками, когда вы начнете синхронизировать свои заявления в f).

Кроме того, мне неясно, что дает вам параллельная коллекция, когда вы изменяете базовую коллекцию во время обработки, поэтому изменяемый буфер списка, в котором могут быть добавлены / удалены элементы, возможно, является плохим выбором. Никогда не знаешь, когда следующий кодер назовет что-то вроде foo(listBuffer) до вашего foreach и передать эту ссылку другому потоку, который может изменить список во время его обработки.

Кроме этого, я думаю, что для любого f это займет много времени, может быть вызвано одновременно и где e могут быть обработаны не по порядку, это прекрасный шаблон.

immutCol.par.foreach { e => threadSafeOutOfOrderProcessingOf(e) }

Отказ от ответственности: я не пытался // собрать себя, но я с нетерпением жду, чтобы ТАК вопросы / ответы показали нам, что работает хорошо.

Этот код довольно странный - зачем добавлять материал параллельно чему-то, что нужно синхронизировать? Вы добавите раздор и ничего не получите взамен.

Принцип действия - накапливать результаты параллельной обработки, лучше достичь с такими вещами, как fold, reduce или же aggregate,

synchronisedList должен быть безопасным, хотя println может дать неожиданные результаты - у вас нет гарантии того, что элементы будут напечатаны, или даже то, что ваши отпечатки не будут чередоваться в середине символа.

Синхронизированный список также вряд ли будет самым быстрым способом сделать это, более безопасное решение - map над неизменной коллекцией (Vector это, вероятно, ваша лучшая ставка здесь), затем напечатайте все строки (по порядку):

val input = Vector("one","two","three","four","five","six","seven","eight","nine")
val output  = input.par.map { e =>
  val msg = "processed :" + e
  // using sleep here to simulate a random delay
  Thread.sleep((math.random * 1000).toLong)
  msg
}
println(output mkString "\n")

Вы также заметите, что этот код имеет столько же практической полезности, что и ваш пример:)

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

Тем не менее, параллельные коллекции обрабатывают элементы одновременно (одновременно) И не в порядке. Вне порядка означает, что элемент 54. может быть обработан до элемента 2. Ваш список синхронизированных массивов будет содержать элементы в неопределяемом порядке.

В общем лучше использовать map, filter и другие функциональные комбинаторы для преобразования коллекции в другую коллекцию - это обеспечит сохранение гарантий упорядочения, если в коллекции есть некоторые (например, Seqс) Например:

ParArray(1, 2, 3, 4).map(_ + 1)

всегда возвращается ParArray(2, 3, 4, 5),

Однако, если вам нужен определенный потокобезопасный тип коллекции, такой как ConcurrentSkipListMap или синхронизированную коллекцию, которая будет передана какому-либо методу в некотором API, модификация его из параллельного foreach безопасна.

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

Другие вопросы по тегам