Semaphore

동시성 프로그래밍에서는 여러 작업들이 공유 자원을 사용하기 때문에, 여러가지 문제가 발생한다. 

이러한 문제들은 둘 이상의 작업들이 공유자원에 접근하면 문제가 생기는 코드 영역Critical Section에서 발생하는데, 

공유자원에 접근하여 발생하는 문제 중 하나로 두 작업의 접근 순서에 따라 결과가 바뀌는 문제가 있다.

for _ in 1...5 {
    let group = DispatchGroup()
    
    var someNumber = 10
    
    DispatchQueue.global().async(group: group) {
        someNumber *= 10
    }
    DispatchQueue.global().async(group: group) {
        someNumber += 1
    }

    group.notify(queue: .main) {
        print(someNumber)
    }
}

/* prints:
 110
 101
 110
 110
 101
*/

이러한 Critical Section에서는 접근할 수 있는 작업(Thread 혹은 Process)의 갯수를 제한해야 하는데,

Semaphore이를 해결하기 위한 방법 중 하나이다.

 

Semaphore는 binary Semaphore, counting Semaphore가 있는데 

DispatchSemaphore에서는 counting Semaphore를 다루기 때문에, 이에 대해 알아보자.

 

Semaphore양의 변수($S$)를 의미하며, 공유자원에 접근 가능한 작업(Thread 혹은 Process)의 갯수를 의미한다.

Semaphore는 두가지 함수로 조작된다.

  • P(wait) : $S$가 0이 아닌 경우, 감소하고, 공유자원에 접근한다. 0인 경우에는 접근하기 않고 대기한다.
  • V(signal) : $S$를 1 증가 시킨다. 작업이 공유자원을 다 사용하고 나왔다는 것을 의미한다.

 

 

DispatchSemaphore

DispatchSemaphore는 counting Semaphore로 구현되었다.

signal()메서드를 통해 semaphore($S$)를 증가시키고, 

wait()메서드를 통해 semaphore($S$)를 감소시킨다.

 

wait 메서드는 DispatchGroup, DispatchWorkItem과 같이 3개의 wait메서드를 가지고 있다.

public func wait()

public func wait(timeout: DispatchTime) -> DispatchTimeoutResult

public func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult

3개의 wait(timeout) 메서드는 시간을 지정해줄 수 있다.

하지만 시간이 완료되었다고 semaphore가 증가하거나 작업이 중단되는 것이 아닌, 

해당 시간내에 작업이 완료되었으면 .success를, 시간내에 작업이 완료되지 않았으면, .timedOut을 리턴한다.

DispatchTime과 DispatchWallTime의 차이점은 DispatchGroup 포스팅을 참고바란다.

 

DispatchSemaphore를 활용하여 앞선 예제를 개선해보자. 

let dispathSemaphore = DispatchSemaphore(value: 1)
for _ in 1...5 {
    let group = DispatchGroup()
    
    
    var someNumber = 10
    
    dispathSemaphore.wait()
    DispatchQueue.global().async(group: group) {
        someNumber *= 10
        dispathSemaphore.signal()
    }
    
    dispathSemaphore.wait()
    DispatchQueue.global().async(group: group) {
        someNumber += 1
        dispathSemaphore.signal()
    }


    group.notify(queue: .main) {
        print(someNumber)
    }
}

/* prints:
 101
 101
 101
 101
 101
*/

 

 

Reference

위키피디아 

 

Apple Documentation

복사했습니다!