티스토리 뷰

Swift

[Swift] Concurrency(5) - Actors

Kim_Baechu 2022. 12. 15. 20:32

태스크를 사용하여 프로그램을 분리된 동시성 조각으로 분할할 수 있습니다.

태스크는 서로 격리되어 있으므로 태스크를 동시에 실행하는 것이 안전하지만 때로는 태스크 간에 일부 정보를 공유해야 합니다.

Actor를 사용하면 동시 코드 간에 정보를 안전하게 공유할 수 있습니다.

 

클래스와 마찬가지로 Actor도 참조 유형이므로 Classes Are Reference Types의 값 유형과 참조 유형 비교는 클래스뿐만 아니라 액터도 적용됩니다.

클래스와 달리 액터는 한 번에 하나의 작업만 변경 가능한 상태에 액세스할 수 있으므로 여러 작업의 코드가 액터의 동일한 인스턴스와 상호 작용하는 것이 안전합니다.

예를 들어, 다음은 온도를 기록하는 액터입니다.

actor TemperatureLogger {
    let label: String
    var measurements: [Int]
    private(set) var max: Int

    init(label: String, measurement: Int) {
        self.label = label
        self.measurements = [measurement]
        self.max = measurement
    }
}

actor 키워드를 사용하여 액터를 소개한 다음, 괄호 쌍으로 액터를 정의합니다.

TemperatureLogger 액터에는 액터 외부의 다른 코드가 액세스할 수 있는 속성이 있으며, 액터 내부의 코드만 최대값을 업데이트할 수 있도록 max 속성을 제한합니다.

구조체 및 클래스와 동일한 초기화 구문을 사용하여 액터의 인스턴스를 만듭니다.

액터의 프로퍼티 또는 메서드에 액세스할 때 await를 사용하여 잠재적인 일시 중단 지점을 표시합니다.

예:

let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
print(await logger.max)
// Prints "25"

이 예에서는 logger.max에 액세스하는 것이 가능한 일시 중단 지점입니다.

액터는 한 번에 하나의 태스크만 변경 가능한 상태에 액세스할 수 있도록 허용하므로 다른 태스크의 코드가 이미 로거와 상호 작용하는 경우 이 코드는 프로퍼티에 액세스하기 위해 기다리는 동안 일시 중단됩니다.

 

대조적으로, 액터의 일부인 코드는 액터의 프로퍼티에 액세스할 때 await를 작성하지 않습니다.

예를 들어, 다음은 TemperatureLogger를 새 온도로 업데이트하는 방법입니다.

extension TemperatureLogger {
    func update(with measurement: Int) {
        measurements.append(measurement)
        if measurement > max {
            max = measurement
        }
    }
}

update(with:) 메서드가 이미 액터에서 실행되고 있으므로 max와 같은 속성에 대한 액세스를 await로 표시하지 않습니다.

또한 이 메서드는 액터들이 한 번에 하나의 작업만 그들의 가변 상태와 상호작용하도록 허용하는 이유 중 하나를 보여줍니다.

액터의 상태에 대한 일부 업데이트는 일시적으로 불변량을 깨트립니다.

TemperatureLogger 액터는 온도와 최대 온도 리스트를 추적하고 새 측정값을 기록하면 최대 온도를 업데이트합니다.

업데이트 도중에 max를 업데이트하기 전에 새로운 측정값을 추가하면 TemperatureLogger가 일시적으로 일관되지 않은 상태가 됩니다.

여러 태스크가 동일한 인스턴스와 동시에 상호 작용하지 못하도록 하면 다음 이벤트 시퀀스와 같은 문제가 발생하지 않습니다.

  1. 코드가 update(with:) 메서드를 호출합니다. 먼저 measurements 배열을 업데이트합니다.
  2. 코드가 max를 업데이트하기 전에 다른 곳에서 코드가 최대값과 온도 배열을 읽습니다.
  3. 코드가 max를 변경하여 업데이트를 마칩니다.

 

이 경우, 데이터가 일시적으로 유효하지 않은 상태에서 액터에 대한 접근이 update(with:) 호출 도중 끼어넣어졌기 때문에 다른곳에서 실행되는 코드는 잘못된 정보를 읽을 수 있습니다.

Swift 액터를 사용할 때 이 문제를 방지할 수 있습니다.

왜냐하면 그들은 한 번에 하나의 상태에 대한 작업만 허용하고 await가 일시 중단 지점을 표시하는 곳에서만 코드가 중단될 수 있기 때문입니다.

update(with:)에 일시 중단 지점이 없기 때문에 다른 코드는 업데이트 도중 데이터에 액세스할 수 없습니다.

클래스 인스턴스에서와 같이 액터 외부에서 이러한 프로퍼티에 액세스하려고 하면 컴파일 오류가 발생합니다.

예:

print(logger.max)  // Error

액터의 프로퍼티 해당 액터의 격리된 로컬 상태의 일부이기 때문에 await를 쓰지 않고 logger.max에 액세스하는 것은 실패합니다.

스위프트는 액터 내부의 코드만이 액터의 로컬 상태에 접근할 수 있음을 보장합다.

이것을 actor isolation라고 합니다.

 

다음글

https://baechukim.tistory.com/156

댓글
공지사항