Swift 동시성 - Swift SDK
이 페이지의 내용
Swift의 동시성 시스템은 구조화된 방식으로 비동기 및 병렬 코드를 작성하기 위한 내장 지원 을 제공합니다. Swift 동시성 시스템에 대한 자세한 개요는 Swift 프로그래밍 언어 동시성 주제 을 참조하세요.
해당 페이지의 고려 사항은 Swift 동시성 기능과 함께 영역을 사용하는 데 광범위하게 적용되지만 Realm Swift SDK 버전 10.39.0에는 Swift 행위자와 함께 Realm을 사용하기 위한 지원이 추가되었습니다. Realm은 단일 행위자에 격리하여 사용하거나 여러 행위자에 걸쳐 사용할 수 있습니다.
Realm의 행위자 지원은 메인 행위자 및 배경 행위자 컨텍스트에서 Realm 사용을 간소화하며 해당 페이지의 동시성 고려 사항에 관한 많은 조언을 대체합니다. 자세한 내용은 행위자와 함께 Realm 사용 - Swift SDK를 참조하세요.
Realm 동시성 주의 사항
앱에서 동시성 기능을 구현할 때 Realm의 스레딩 모델과 Swift 동시성 스레딩 동작에 대한 이 주의 사항을 고려하세요.
Await를 사용하여 실행 일시 중지
Swift 키워드를 사용하는 모든 곳에서 await
은(는) 코드 실행의 가능한 일시 중단 지점을 표시합니다. Swift 5.7에서는 코드가 일시 중단되면 후속 코드가 동일한 스레드에서 실행되지 않을 수 있습니다. 즉, 코드에서 await
을(를) 사용하는 모든 곳에서 후속 코드는 앞이나 뒤에 오는 코드가 아닌 다른 스레드에서 실행될 수 있습니다.
이는 본질적으로 영역의 라이브 객체 패러다임과 호환되지 않습니다. 라이브 객체, 컬렉션 및 영역 인스턴스는 스레드에 한정되어 있으므로 해당 인스턴스가 생성된 스레드에서만 유효합니다. 실질적으로 이는 라이브 인스턴스를 다른 스레드로 전달할 수 없음을 의미합니다. 그러나 영역은 스레드 간에 객체를 공유하기 위한 몇 가지 메커니즘을 제공합니다. 이러한 메커니즘은 일반적으로 스레드 간에 데이터를 안전하게 전달하기 위해 코드에서 몇 가지 명시적 처리를 수행해야 합니다.
고정된 객체 또는 ThreadSafeReference와 같은 일부 메커니즘을 통해 await
키워드를 사용하여 스레드 전체에서 Realm 객체와 인스턴스를 안전하게 사용할 수 있습니다. 또한 비동기 Realm 코드를 @MainActor
(으)로 표시하여 앱이 항상 메인 스레드에서 이 코드를 실행하도록 함으로써 스레딩과 관련된 문제를 방지할 수 있습니다.
일반적으로 스레딩 보호 기능을 통합하지 않고 await
컨텍스트에서 Realm을 사용하면 일관성 없는 동작이 발생할 수 있습니다. 때로는 코드가 성공할 수도 있습니다. 다른 경우에는 잘못된 스레드에 쓰기와 관련된 오류가 발생할 수 있습니다.
비동기/대기 API
Atlas App Services 앱 또는 동기화된 영역으로 작업하는 많은 Realm Swift API는 비동기/대기 구문과 호환됩니다. 다음 예시를 확인해보세요.
Swift 비동기/대기 API와 관련된 특정 기능 요청이 있는 경우 Realm용 MongoDB 피드백 엔진을 확인하세요. Realm Swift SDK 팀은 커뮤니티 피드백과 Swift 동시성 진화를 기반으로 동시성 관련 기능을 계속 개발할 계획입니다.
백그라운드 쓰기 행
일반적으로 요청되는 비동기 코드 사용 사례는 기본 스레드를 차단하지 않고 백그라운드에서 쓰기 작업을 수행하는 것입니다.
Realm에는 비동기 쓰기를 수행할 수 있는 API 두 가지가 있습니다.
writeAsync() API를 사용하면 Swift 완료 핸들러를 사용하여 비동기 쓰기를 수행할 수 있습니다.
asyncWrite() API를 사용하면 Swift 비동기/대기 구문을 사용하여 비동기 쓰기를 수행할 수 있습니다.
이 두 API를 통해 고정된 객체를 사용하거나 스레드로부터 안전 참조를 전달하지 않고도 백그라운드에서 객체를 추가, 업데이트 또는 삭제할 수 있습니다.
writeAsync()
API를 사용하면 쓰기 잠금(write lock)을 받기 위해 대기하고 트랜잭션을 커밋하는 작업이 백그라운드에서 발생합니다. 쓰기 차단 자체는 호출 스레드에서 실행됩니다. 이는 고정된 객체를 수동으로 처리하거나 스레드 간에 참조를 전달할 필요 없이 스레드 안전성을 제공합니다.
그러나 쓰기 차단이 실행되는 동안 호출 스레드에서 새 트랜잭션이 차단됩니다. 즉, writeAsync()
API를 사용하는 대규모 쓰기는 실행되는 동안 작고 빠른 쓰기를 차단할 수 있습니다.
asyncWrite()
API는 스레드를 차단하는 대신 쓰기 차례를 기다리는 동안 호출 작업을 일시 중단합니다. 또한 데이터를 디스크에 쓰기 위한 실제 I/O는 백그라운드 작업자 스레드에서 수행됩니다. 소규모 쓰기의 경우 메인 스레드에서 이 기능을 사용하면 쓰기를 백그라운드 스레드에 수동으로 전달하는 것보다 짧은 시간 동안 메인 스레드를 차단할 수 있습니다.
코드 예시를 포함한 자세한 내용은 백그라운드 쓰기 수행하기를 참조하세요.
작업 및 작업 그룹
Swift 동시성은 작업 관리를 위한 API를 제공합니다. 및 TaskGroups . Swift 동시성 문서 는 작업을 프로그램의 일부로 비동기적으로 실행할 수 있는 작업 단위로 정의합니다. 작업을 사용하면 비동기 작업의 단위를 구체적으로 정의할 수 있습니다. TaskGroup을 사용하면 상위 TaskGroup 아래에서 실행할 Tasks 컬렉션을 단위로 정의할 수 있습니다.
작업 및 TaskGroups는 스레드를 다른 중요한 작업에 양보하거나 다른 작업을 차단할 수 있는 장기 실행 작업을 취소하는 기능을 제공합니다. 이러한 이점을 얻으려면 작업 및 TaskGroups를 사용하여 백그라운드에서 영역 쓰기를 관리할 수 있습니다.
그러나 위의 Suspending Execution with Await (대기 상태로 실행 일시 중단)에 설명된 스레드 제한 제약 조건은 작업 컨텍스트에 적용 . 작업에 await
지점이 포함된 경우 후속 코드가 다른 스레드에서 실행 되거나 재개되어 Realm의 스레드 제한을 위반할 수 있습니다.
Realm에 액세스하는 코드가 메인 스레드에서만 실행되도록 하려면 @MainActor
을(를) 사용하여 작업 컨텍스트에서 실행하는 함수에 주석을 달아야 합니다. 이로 인해 작업 기능 사용의 일부 장점을 활용하지 못하게 되며 사용자 관리와 같은 네트워킹 활동에만 작업을 사용하는 경우가 아니라면 Realm을 사용하는 앱에서는 좋은 디자인 선택이 아니라는 것을 의미할 수 있습니다.
행위자 격리
팁
'Swift 행위자와 함께 Realm 사용' 또한 참조하세요.
이 섹션의 정보는 Realm SDK 10.39.0 이전 버전에 적용됩니다. Realm Swift SDK 버전 10.39.0 이상부터 SDK는 Swift 행위자 및 관련 비동기 기능과 함께 Realm을 사용할 수 있도록 지원합니다.
자세한 내용은 행위자와 함께 Realm 사용 - Swift SDK를 참조하세요.
행위자 격리는 Realm 액세스를 전용 행위자로 제한하는 인식을 제공하므로 비동기 컨텍스트에서 Realm 액세스를 관리하는 안전한 방법처럼 보입니다.
그러나 @MainActor
이(가) 아닌 비동기 함수에서 Realm을 사용하는 것은 현재 지원되지 않습니다.
이는 Swift 5.6에서 종종 우연히 작동합니다. await
이후의 실행은 대기 중인 항목이 실행된 스레드에서 계속됩니다. 비동기 함수에서 await Realm()
을(를) 사용하면 다음에 행위자 격리 함수를 호출할 때까지 메인 스레드에서 코드를 실행하게 됩니다.
대신 Swift 5.7은 행위자 격리 컨텍스트를 변경할 때마다 스레드를 홉합니다. 격리되지 않은 비동기 함수는 항상 백그라운드 스레드에서 실행됩니다.
await Realm()
을(를) 사용하고 5.6에서 작동하는 코드가 있는 경우 함수를 @MainActor
(으)로 표시하면 Swift 5.7에서 작동하게 됩니다. 5.6에서 의도치 않게 작동하던 방식 그대로 작동합니다.
동시성 코드 관련 오류
대부분의 경우 동시성 코드를 통해 Realm에 액세스하는 것과 관련하여 표시되는 오류는 Realm accessed from incorrect thread.
입니다. 이는 이 페이지에 설명된 스레드 격리 문제 때문입니다.
Swift 동시성 기능을 사용하는 코드에서 스레딩 관련 문제를 방지하려면 다음을 수행합니다.
행위자가 격리된 영역을 지원하는 Realm Swift SDK 버전으로 업그레이드하고 이를 수동으로 스레딩을 관리하는 대신 사용할 수 있습니다. 자세한 내용은 행위자와 함께 Realm 사용 - Swift SDK를 참조하세요.
영역에 액세스할 때 실행 컨텍스트를 변경하지 않도록 합니다. UI에 데이터를 제공하기 위해 메인 스레드에서 영역을 여는 경우
@MainActor
을(를) 사용하여 영역에 비동기적으로 액세스하는 후속 함수에 주석을 달아 해당 영역이 항상 메인 스레드에서 실행되도록 합니다.await
은(는) 다른 스레드로 변경될 수 있는 일시 중지 지점을 표시한다는 점을 기억합니다.행위자 격리 영역을 사용하지 않는 앱은
writeAsync
API를 사용하여 백그라운드 쓰기를 수행할 수 있습니다. 이렇게 하면 사용자가 직접 특수 코드를 작성할 필요 없이 스레드 안전 방식으로 영역 액세스를 관리할 수 있습니다. 이는 비동기 컨텍스트에서 실행하기 위해 쓰기 프로세스의 일부(안전한 경우)를 아웃소싱하는 특수 API입니다. 행위자가 격리된 영역에 쓰는 경우가 아니라면 Swift의async/await
구문과 함께 이 메서드를 사용하지 않습니다. 코드에서 이 메서드를 동기식으로 사용합니다. 또는 비동기 영역에 쓰기를 기다릴 때 Swift의async/await
구문과 함께asyncWrite
API를 사용할 수 있습니다.영역 액세스가 스레드 안전 방식으로 수행되는 경우 행위자가 격리되지 않은 동시성 코드를 명시적으로 작성하려면 해당되는 스레드 간에 인스턴스를 명시적으로 전달하여 스레드 관련 충돌을 방지할 수 있습니다. 이를 위해서는 Realm의 스레딩 모델을 잘 이해하고 Swift 동시성 스레딩 동작을 염두에 두어야 합니다.
전송 가능, 전송 불가능 및 스레드 제한 유형
Realm Swift SDK 퍼블릭 API에는 크게 세 가지 범주로 분류되는 유형이 포함되어 있습니다.
전송 가능
전송 불가능 및 스레드 제한 없음
스레드 제한
전송이 불가능한 유형과 스레드 간에 스레드로 제한되지 않은 유형을 공유할 수 있지만 이를 동기화해야 합니다.
스레드에 한정된 유형은 동결되지 않는 한 격리 컨텍스트에 한정됩니다. 동기화를 사용해도 이러한 컨텍스트 간에 전달할 수 없습니다.
전송 가능 | Non-Sendable | 스레드 제한 |
---|---|---|
AnyBSON | RLMAppConfiguration | AnyRealmCollection |
AsyncOpen | RLMFindOneAndModifyOptions | AnyRealmValue |
AsyncOpenSubscription | RLMFindOptions | 목록 |
RLM API 키 인증 | RLM 네트워크 전송 | Map |
RLMApp | RLMRequest | MutableSet |
RLMAsyncOpenTask | RLMResponse | 프로젝션 |
RLMChangeStream | RLMSyncConfiguration | RLMArray |
RLMCompensatingWriteInfo | RLMSyncTimeoutOptions | RLMChangeStream |
RLMCredentials | RLMDictionary | |
RLMDecimal128 | RLMDictionaryChange | |
RLMEmailPasswordAuth | RLMEmbeddedObject | |
RLM Max 키 | RLM 링크 오브젝트 | |
RLM Min 키 | RLMObject | |
RLM Mongo 클라이언트 | RLM 속성 변경 | |
RLM Mongo 컬렉션 | RLMRealm | |
RLM Mongo 데이터베이스 | RLMResults | |
RLM 객체 ID | RLM 섹션 | |
RLM 객체 스키마 | RLM 섹션 결과 | |
RLM 진행 알림 | RLM 섹션 결과 변경 세트 | |
RLM 진행 알림 토큰 | RLMSet | |
RLM 속성 | RLM Sync 구독 | |
RLMPropertyDescriptor | RLM Sync 서브스크립션 세트 | |
RLM 제공자 클라이언트 | RealmOptional | |
RLM 푸시 클라이언트 | RealmProperty | |
RLM 스키마 | ||
RLMSortDescriptor | ||
RLM Sync 오류 액션 토큰 | ||
RLM Sync 관리자 | ||
RLM Sync 세션 | ||
RLMThreadSafeReference | ||
RLMUpdateResult | ||
RLM 사용자 | ||
RLM 사용자 API 키 | ||
RL 사용자 ID | ||
RLM 사용자 프로필 | ||
ThreadSafe |