Concurrency - 바다
- Task - 보트
보트(Task)간 통신엔 Sendable 프로토콜을 준수하는 타입만 가능하다. - Sendable
Sendable은 말 그대로 보낼수 있는 상태를 의미함 → data race를 발생시키지 않고 안전하게 공유할 수 있는 데이터 타입
-> Struct, Enum 과 같은 값타입들을 포함하는 것들
참조타입(Class)은 제한된 경우(final class가 immutable storage만을 가진 상황) 를 제외하곤 Sendable일 수 없다.
- 이를 무시하기 위해서 @unchecked Sendable을 사용할 수 있다. 물론 문제에 대한 책임또한 생긴다.
Task 클로저 내부엔 Sendable이 명시되어 있기 때문에 새로운 클로저를 생성할 때 이전 컨텍스트에서 무언가를 캡처하는 경우, Sendable을 검사한다.
이렇게 공유하는 데이터간 안전성을 Sendable을 통해서 보장했다. 그러나 하나의 공유 데이터를 여러 Task에서 접근하게 될 경우 문제가 발생할 수 있다. - 이것을 해결하기 위한 Actor - 섬
Actor는 서로 다른 task에서 공유 데이터에 접근하는 방법을 data race를 발생시키지 않고 조정하는 방식으로 제공한다.
이 섬 내부에 데이터들은 모두 독립적이고, 이 독립적인 데이터를 핸들링하기 위해선 섬 내부(Actor isolated) 에서만 핸들링해야 한다
→ actor 내부 메서드만 actor의 인스턴스에 직접 접근할 수 있다.
Task(보트)에서 actor(섬)과 상호작용하기 위해선 섬 앞에 await 키워드를 붙여줘야한다 - actor은 동시적인 접근을 제한하고 한번에 하나의 Task만 상호작용하기 때문에 다른 Task 는 대기해야 한다.
당연히 non-Sendable 타입은 상호작용할 수 없다.
- actor에 대한 참조 - 섬에 대한 지도
- Actor isolated
actor 외부는 당연히 nonisolated 하기 때문에 내부 프로퍼티에 직접 접근이 불가능하고, 외부에서 await 키워드를 통하여 간접적으로만 접근 가능하다.
actor 내부의 함수를 nonisolated 키워드를 사용해 actor 외부로 옮길 수 있다 - 근데 왜?
- 액터 외부에서 필요한 비동기 작업: 액터 내부의 함수가 액터 외부에서 호출되어야 하고, 해당 작업이 액터의 Isolated context와 직접적인 연관이 없을 때 유용합니다. 예를 들어, 액터가 네트워크 요청을 처리하는 함수를 갖고 있을 수 있습니다. 이 함수는 액터 외부에서 호출되어야 하며, 네트워크 요청은 액터의 Isolated context와 독립적으로 수행될 수 있습니다. 이 경우 nonisolated 키워드를 사용하여 액터 외부로 함수를 옮길 수 있습니다.
- 액터 내부 상태 변경이 필요 없는 경우: 액터 내부의 함수가 액터의 상태를 변경하지 않고 단순히 연산 또는 결과 반환에만 관여하는 경우에도 nonisolated 키워드를 사용하여 액터 외부로 함수를 이동할 수 있습니다. 이는 액터의 Isolated context를 필요로 하지 않는 작업을 다른 곳에서 처리하기 위한 방법입니다.
이상 Chat GPT의 대답
- MainActor - 바다의 중간에 있는 큰 섬
유저 인터페이스와 관련된 메인 스레드에서 실행되는 Actor
- Actor isolated
- Atomicity
- 고수준의 data races를 막기위한 방법 - 고수준의 data races는 예시 참조
→ await 구문은 작업이 어디선가 일시 중단될 수 있고, actor가 다른 높은 우선순위의 작업을 처리하며 예사이 못한 데이터 손상을 발생시킬 수 있다. - Ordering - 일관된 순서로 이벤트를 처리하고싶다
Swift Concurrency는 작업을 순서대로 처리하기 위한 도구를 제공하지만, actor은 아니다.
Actor은 가장 높은 우선순위의 작업을 먼저 실행하여, 전체 시스템이 반응적으로 유지되도록 돕는다.
Task 는 제어 흐름을 따라 시작부터 끝까지 실행되므로 자연스럽게 작업을 순서대로 처리할 수 있다.
AsyncStream은 실제 이벤트 스트림을 모델링하는 데 사용할 수 있다. 하나의 Task가 for-await-in 루프를 통해 스트림의 이벤트들을 반복하면서 각 이벤트를 차례대로 처리할 수 있다. - Sendability
Swift 5.7에선 Sendability를 엄격하게 체크할 수 있는 빌드 설정이 도입되었다
→ Strict Concurrency Checking- Minimal - 기본 설정
Sendable로 표시한 경우에만 오류가 아닌 “경고” 로 제공됨 - Targeted - 기존 코드와의 호환성과 잠재적인 경쟁을 식별하는 것 사이의 균형을 유지하기
async/await, tasks, 또는 actors와 같은 Swift Concurrency 기능을 이미 채택한 코드에 대해 Sendable 체크를 활성화한다.
@preconcurrency 속성을 통해 임시적으로 경고를 비활성화 할 수 있다. - Complete - 잠재적인 모든 데이터 경쟁을 드러내기
점진적으로 엄격한 동시성 검사를 통한 오류를 제거하는 방식으로 data race safety한 앱을 달성하도록 권장한다.
- Minimal - 기본 설정
'Swift > Concurrency' 카테고리의 다른 글
Discover Concurrency (3) - Async/await을 사용할 때 메모리 관리하기 (0) | 2023.05.17 |
---|---|
Discover Concurrency (2) - Asynchronous Task 지연시키기 (0) | 2023.05.17 |
Discover Concurrency (1) - Task는 Swift Concurrency system내에서 어떤 역할을 할까? (0) | 2023.05.16 |
WWDC 2022 - Eliminate data races using Swift Concurrency (2) | 2023.05.10 |
Swift Concurrency - Perform asynchronous operation (0) | 2023.05.10 |