Sign In
한결
Status
Empty
Assignee
Empty
동시성의 기본
동시성이라는건 왜 필요할까?
하나의 프로덕트에서 여러 프로그램이 여러 프로세스를 동시에 처리(멀티 프로세스)할 수 있는 환경이 필요할 수 있기 때문에
ex. 나의 아이폰에서 음악앱을 켜 음악을 들으면서 + 카톡도 보내고 + 네이버에 검색도 하는 환경이 독립적이면 너무 귀찮다.
운영체제는 메모리를 이용해서 여러 프로세스를 동시적으로 처리하는 환경을 갖춘다.
메모리에서는 하나의 프로세스안에서 코드/데이터/힙 영역 + 스레드라고 하는 스택을 가진 태스크 처리 영역이 구성된다.
스레드에 등록된 태스크들을 처리해가면서 앱이라고 하는 프로그램의 메인 프로세스를 돌린다.
동시적으로 스레드가 일을 처리하면 어떤 문제가 날 수 있을까?
Data Race
여러 스레드에서 동시에 동일한 값에 접근할 떄 의도하지 않게 값이 훼손될 수 있는 상황
swift에서는 actor 객체를 통해서 의도적인 데이터 접근의 임계치를 만들어 비동기 환경에서 하나의 작업 스레드만 데이터에 접근할 수 있는 환경을 만든다.
actor를 쓰지 않는다면, 의도적으로 하나의 스레드만 한 번에 접근할 수 있도록 신경을 써줘야 한다.
Mutex 방식으로 Lock이라고 하는 데이터 접근 권한을 관리하여 각기 다른 스레드가 독립적으로 접근하도록 제어할 수 있다.
Mutex 방식에서는 서로 다른 스레드가 Lock을 취득하려고 하는 SpinLock 상태가 발생할 수 있고,
특정 스레드는 무한히 작업을 대기하는 Starvation 기아 상태가 발생할 수 도 있다.
iOS18+ 환경에서 Swift 문법 자체적으로 제공하는 Mutex 구조체를 활용할 수도 있다. 그 이전 버전에서는 NSLock을 통해 구현해볼 수 있다.
Semaphore 방식으로도 하나의 데이터에 여러 스레드의 접근을 제한시킬 수 있다.
semaphore라고 하는 접근 가능한 횟수를 담은 변수를 이용해서 접근하는 스레드의 수를 제한하고, 오버되는 스레드는 대기 Queue에 담아 순차적으로 접근할 수 있게 처리한다.
swift의 Dispatch 프레임워크에서 DispatchSemaphore API를 통해 Semaphore 객체로 관리할 수 있다. DispatchSemaphore(value: 1)
DeadLock
데드락이 발생할 수 있는 조건은 다양할 수 있다. (상호배제, 비선점, 순환적 대기, 점유 대기)
swift에서는 메인에 서로 다른 스레드의 작업이 동시에 직렬적으로 등록된다면 deadlock이 걸릴 수 있다.
쉽게 생각해서 (UIKit 예를 생각해보면), viewDidLoad되는 상황에서는 main 스레드가 뷰를 그려내는 작업을 하고 있는데,
그 과정에서 DispatchQueue.main.sync로 억지로 직렬적 작업을 추가하는 경우 데드락이 발생할 수 있겠다.
그러면, 데드락 상태는 어떻게 해결 또는 예방해볼 수 있을까?
데드락이 발생하는 조건이 충족되지 않도록 코드를 짜면 된다. (간단)
또는, 데드락이 발생할 것 같은 환경이 있는지 미리 확인해서 태스크가 돌지 않도록 처리할 수 있겠다. (banker algorithm)
Swift Concurrency 의 등장은 동시성에 어떤 영향을 주었을까?
async로 선언된 작업은 await 구문을 만나기 전까지는 동시성의 영역에서 stack 공간을 차지하지 않는다.
즉, 태스크를 선언한 스레드와 태스크가 처리되는 스레드가 동일하지 않을 수 있다는 뜻이다.
await 구문이 동작할 수 있는 Task { } 컨택스트나, 또 다른 async한 환경이 아니라면 동시에 다른 일을 처리하도록 할 수 있다.
동시성에 따라 발생할 수 있는 Data Race 상황은 Actor 객체를 통해서 스레드의 접근을 await로 강제할 수 있게 한다.
👍