[swift] 스레드의 동시성 제어 기법(lock, semaphore, monitor 등)

스레드는 동시에 실행되기 때문에 데이터의 일관성과 안정성을 보장하기 위해서는 동시성 제어가 필요합니다. 스레드의 동시성 제어 기법에는 여러 가지가 있지만, 여기서는 가장 일반적인 기법인 락(lock), 세마포어(semaphore), 모니터(monitor)에 대해 알아보겠습니다.

락(Lock)

락은 가장 간단한 동시성 제어 기법입니다. 락은 코드 영역을 보호하여 한 스레드가 해당 영역을 점유하고 있을 때 다른 스레드가 접근하지 못하도록 합니다. 스레드가 해당 영역을 더 이상 사용하지 않을 때 락을 해제하여 다른 스레드가 접근할 수 있게 합니다.

let lock = NSLock()

lock.lock()
// 동시성 제어가 필요한 코드 영역
lock.unlock()

락은 명시적으로 잠금과 해제를 호출해야하기 때문에 실수로 락을 잃어버리거나 두 개 이상의 락을 동시에 잡는 문제가 발생할 수 있습니다.

세마포어(Semaphore)

세마포어는 락보다 더 유연한 동시성 제어 기법입니다. 세마포어는 정해진 개수의 동시 접근을 허용하고, 사용 가능한 토큰의 개수를 추적합니다. 한 스레드가 세마포어에 접근하면 토큰이 소비되고, 다른 스레드가 토큰을 가져와 해당 영역에 접근할 수 있습니다.

let semaphore = DispatchSemaphore(value: 1)

semaphore.wait()
// 동시성 제어가 필요한 코드 영역
semaphore.signal()

세마포어는 특정 리소스의 동시 접근 허용 개수를 조절할 수 있어 더 큰 유연성을 제공하지만, 적절한 토큰 관리가 필요하며 복잡성이 증가할 수 있습니다.

모니터(Monitor)

모니터는 락과 세마포어의 개념을 통합한 것으로, 한 번에 한 스레드만이 모니터에 접근할 수 있도록 합니다. 모니터는 상호 배제와 조건 변수를 사용하여 동시성을 제어합니다.

class Monitor {
    private var lock = NSLock()
    private var condition = NSCondition()
    private var value = 0
    
    func increment() {
        lock.lock()
        // 동시성 제어가 필요한 코드 영역
        value += 1
        condition.broadcast()
        lock.unlock()
    }
    
    func waitUntilValueGreaterThan(target: Int) {
        lock.lock()
        while value <= target {
            condition.wait()
        }
        lock.unlock()
    }
}

모니터는 내부적으로 락과 조건 변수를 사용하여 동시성 제어를 수행합니다. 상태 변경 및 조건 검사 메서드를 사용하여 스레드 간의 통신과 동기화를 관리할 수 있습니다.

결론

락, 세마포어, 모니터는 스레드의 동시성을 제어하기 위해 사용되는 기법입니다. 각각의 기법은 서로 다른 장점과 특징을 가지고 있으며, 상황에 맞게 적절한 기법을 선택하고 사용해야 합니다.

참고 자료: