Swift Concurrency 11편 - DataRace 방지 편

김재형·2024년 7월 10일
0
post-custom-banner

시작 하기에 앞서 SwiftConcurrency 1편부터 이어지는 내용임으로 1편부터 보고 와주시길 바랍니다!
시작 하겠습니다.

Task Isolation

각 비동기 작업이 독립적으로 실행되도록 합니다.
독립적 이기에 데이터 레이스를 방지 할수 있죠.

순차적 (Sequential)

각 Task는 독립적으로 실행됩니다. Task 간 순차적으로 실행됨으로,
각 Task는 자신만의 흐름을 가지고 있습니다.
작업은 처음부터 끝까지 순차적으로 수행함을 보장합니다.

비동기적 (Asynchronous)

Task에서 비동기적으로 실행됩니다. 다른 Task의 완료를 기다리지 않고 병렬로 실행 될수 있죠.

유연한 일체형 (Self-contained)

각 Task는 스스로 상태,실행 흐름을 가지고 있어, 다른 Task와 독립적으로 동작합니다.
즉, Task 간의 간섭을 방지합니다.

예시

Task {
    var localData = "떼이타"
    localData += " 추가염"
    print(localData) // "떼이타 추가염"
}
Task {
    var localData = "Swift"
    localData += " Concurrency"
    print(localData) // "Swift Concurrency"
}

Actor Isolation

Actor 내부 상태는 직렬화된 접근이 보장되며, 서로 다른 작업에서 접근 할 수 있는 상태를 격리 시켜 data race를 제거할수 있습니다.

캡슐화 (Encapsulation)

외부에서 접근할수 없도록 상태를 캡슐화 합니다.

직렬화 (Serialized)

actor에 대한 접근(상태 접근)은 직렬화되므로, 동시에 여러 Task가 동일한 상태를 변경할 수 없습니다.

Sendable

모든 actorsendable을 압시적 준수 합니다. actor는 참조 타입이지만, 내부의 상태를 격리하여, 독립성을 갖습니다.

Task.detached와 Actor의 독립성

Task.detached 는 독립적인 비동기 작업을 생성합니다.
actor의 독립성을 종속하지 않습니다. 즉 외부에 있는것으로 간주됩니다.
다만, Actor 상태를 직접 변경 불가능 하지만, 메서드를 통해서는 가능합니다.

actor Kitchen {
    private var food: String = "Apple"

    func getFood() -> String {
        return food
    }

    func setFood(newFood: String) {
        food = newFood
    }
}

let kitchen = Kitchen()

// Task를 사용하여 Actor의 상태에 접근
Task {
    // Task는 현재 컨텍스트를 상속받기 때문에 Actor의 상태에 접근할 수 있습니다.
    await kitchen.setFood(newFood: "Banana")
    print(await kitchen.getFood()) // "Banana"
}

// Task.detached를 사용하여 Actor의 상태에 접근
Task.detached {
    // Task.detached는 Actor의 격리된 상태에 직접 접근할 수 없습니다.
    // Actor의 메서드를 통해서만 접근할 수 있습니다.
    print(await kitchen.getFood()) // "Banana"
}

@MainActor

메인 쓰레드에서 동작함을 나타내는 Actor 입니다.
메인 쓰레드에서 해야하는 UI작업은 MainActor에서 해줘야 합니다.

Atomicity

작업이 손실 되지 않고 완료 될수 있도록 보장하는 방법입니다.

Low-Level Data Race 제거

  • 데이터 손상을 방지하기 위해 low-level 데이터 레이스를 제거합니다.

동기적인 코드로 변환:

  • 비동기 메서드는 Actor 내부에서 동기적인 코드로 변환되어야 합니다.
actor Human {
    var Hands: [Hand] = []

    func deposit(Hands: [Hand]) async {
        var current = self.Hands
        current += Hands
        self.Hands = current
    }
}

let island = Island()

Task {
    var apples: [Hand] = [Hand(), Hand()]
    await island.deposit(apples: apples)
}

마치며....

저번에 다루었던 내용을 더 다듬어서 적용해 보았는데
이해가 되셨을까요..? 궁금한 사항이 있다면 댓글로 남겨주세요!
다음 편은 distributed actor 로 돌아오겠습니다!

profile
IOS 개발자 새싹이
post-custom-banner

0개의 댓글