나는 동시성 문제에 익숙하지 않으므로 나를 맨손으로 생각한다. 스칼라 맵을 보조 저장소로 사용하는 스레드 안전 컨테이너를 만들고 싶습니다. 사용자를 기본 Map에 노출하는 대신 해당 메소드의 하위 집합 만 노출합니다.불변 콜렉션을 래핑하고 스칼라에서 스레드 안전을 유지 하시겠습니까?
예
class MyContainer[A] {
def add(thing: A): Unit = {
backingStore = backingStore + (thing.uuid -> thing)
}
def filter(p: A => Boolean): Option[Iterable[A]] = {
val filteredThings = backingStore.values.filter(p)
if (filteredThings.isEmpty) None else Some(filteredThings)
}
def remove(uuid: UUID): Option[A] = backingStore.get(uuid) match {
case optionalThing @ Some(thing) =>
backingStore = backingStore - uuid; optionalThing
case None => None
}
@ volatile private[this] var backingStore = immutable.HashMap.empty[UUID, A]
}
... 나는 기본 백업 저장소는 불변이고 그것의 참조
volatile
경우에도 컨테이너가 스레드로부터 안전하지 않습니다 의심 ... 다음과 같은 형태가 될 것이다.
위 컨테이너의 인스턴스에 대한 액세스 권한으로 실행중인 두 개의 개별 스레드가 있다고 가정합니다. Thread 1은 기본 컬렉션을 필터링하고 몇 가지 결과를 얻습니다. 동시에 스레드 2는 항목을 제거합니다. 스레드 1에있는 결과에는 스레드 2가 제거한 항목에 대한 참조가 포함될 수 있습니까? 다른 문제가있을 수 있습니다.
위의 구현이 스레드로부터 안전하지 않다고 수정합니까? 스칼라를 사용하여 위의 스레드 안전성을 만드는 가장 관용적 인 방법은 무엇입니까?
편집 : 가능한 경우 차단 및 동기화를 피하는 것이 좋습니다. 차단/동기화를 사용해야하는 경우 휘발성 참조가 필요합니까? 불변 컬렉션의 핵심은 무엇입니까? 변경 가능한 컬렉션을 사용할 수 없었습니까?
@ladams; 하지만 필터와 같은 읽기 메소드의 경우에는 동기화 할 필요가 없습니다 (실제로 읽기 및 쓰기의 순서는 신경 쓰지 않는 한). – davidrpugh
@ladams 만약'add'와'remove' 메소드 만 sychronize했다면, 여전히 @volatile로 백업 저장소에 대한 참조에 주석을 달 필요가 있습니까? – davidrpugh
예. 추가/제거 방법을 모두 동기화해야합니다. 참조를 휘발성으로 표시하면 업데이트 가시성이 적시에 나타납니다.이 작업을하지 않으면 판독기 스레드가 다른 스레드가 업데이트 된 후 일정하지 않은 시간 동안 백업 저장소의 이전 버전에 대한 참조를 유지할 수 있습니다 잠재적으로 여러 번. 다음과 같은 두 가지 작업을 수행해야하는 상황에서 다른 스레드에 대한 가시성을 확보하려면 다음과 같이 두 가지 작업을 수행해야합니다. 작성자가 프로세서 캐시에서 주 메모리로 메모리 쓰기 (동기화 블록 종료시 발생) 힘. – Iadams