Swift 데이터 경쟁 피하기
- 데이터 경쟁(data race)란
- 멀티 쓰레드/프로세스 환경에서 일어나는 오류이며 여러 쓰레드/프로세스 가 공유자원에서 동시에 접근하려 할때 일어나는 경쟁 상황을 일컫는다.
- 예를들어 한 쓰레드와 또 다른 쓰레드가 동시에 한 변수를 쓰려고 할때, 한 쓰레드는 한 변수를 쓰고 있는데 다른 쓰레드는 그 변수를 읽으려 할 때 등의 상황에서 데이터 레이스 상황이 발생한다.
- 경쟁 상태(Race Condition)란
- 두개 이상의 프로세스가 공통 자원을 병행적으로(concurrently) 읽거나 쓸때, 공용 데이터에 대한 접근 권한이 어떤 순서에 따라 이루어졌는지에 따라 그 실행 결과가 달라지는 상황을 말한다.
- 즉 두 개의 스레드가 하나의 공유 자원을 놓고 서로 사용하려고 경쟁하는 상황을 의미한다.
- 최종으로 데이터 경쟁은 경쟁 상태의 상황을 의미하는 것이며 이러한 결과물(return) 값이 경쟁 상태를 생각 하면 된다. 때문에 데이터 경쟁의 어떠한 실행 결과에 따라 경쟁 상태의 결과 값이 달라진다.
func doSomething() async {
var timeStamps: [Int: Date] = [:]
print("Start \(Date())")
await withTaskGroup(of: Void.self) { group in
for i in 0...5 {
group.addTask {
timeStamps[i] = await tasksTooLong()
}
}
}
}
Swift Race Condition 피하기
- Race Condition을 피하는 방법은
Thread Safe하게 하는 방법이며 방법은 다음과 같습니다.
- DispatchSemaphore
- Custom Serial Queue
- NSLock
- DispatchSemaphore 는 counting semaphore 방식으로 자원에 접근이 가능하기에 스레드를 수적으로 제한 할 수 있습니다. 즉 1개의 스레드만 접근하도록 허용한다면 Race Condition 을 해결 할 수 있습니다.
- Custom Serial Queue 를 이용하여 Task를 줄세우면, DispatchSemaphore 와 마찬가지로 자원에 1개의 스레드만 접근하게 할 수 있습니다.
- NSLock 은
Thread Safe 를 지키는 방법 중 하나인 atomic operations 을 위한 방법이며 lock() 메서드를 통해 이미 해당 메서드가 호출되고, unlock() 메서드가 호출되기 전까지 다른 Thread는 기다리게 되면서, 한 자원에 하나의 Thread가 접근할 수 있게 해주는 객체이다.