본문 바로가기
아이폰 개발/Swift

swift DispatchGroup 과 DispatchSemaphore - 2

by 인생여희 2021. 9. 25.

swift DispatchGroup 과 DispatchSemaphore - 2

 

[*] for 문을 이용해서 비동기 함수를 호출했을때 문제상황과 해결법

 

문제

아래 print 함수는 fetchData 함수가 리턴되는 콜백함수를 호출하기 전에 호출되어서 "text: " 으로 출력된다.

 

// 지연 후에 Int를 String으로 변환하는 함수
func fetchData(_ data: Int, delay: Double, completionHandler: @escaping (String)->()) {
    
    print("fetchData - 진입 ")
    
    DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
        
        print("fetchData - data  \(data) ")
        
        completionHandler("\(data)")
    }
    
    print("fetchData - 종료 ")

}

var text = ""
for i in 0..<20 {
    fetchData(i, delay: Double.random(in: 0...0.2)) {
        text += "\($0) - "
    }
}

print("text:", text)

 

해결  - 1

DispatchGroup 사용

 

for 문 안의 모든 fetchData 함수가 동시에 호출되기 때문에, 모든 콜백이 완료되고 group.leave()를 보낸 후 group.notify()가 실행된다.

그래서 이번에는 print 문에 값은 출력 된다.

 

3 - 7 - 16 - 8 - 1 - 0 - 6 - 14 - 10 - 13 - 4 - 11 - 18 - 9 - 19 - 5 - 12 - 2 - 17 - 15 -

 

문제가 있다면 출력값이 순서대로 출력된것이 아니라 무작위로 출력이 되었다는 점이다.

어떻게 해결할 수 있을까?

 

import Foundation


// 지연 후에 Int를 String으로 변환하는 함수
func fetchData(_ data: Int, delay: Double, completionHandler: @escaping (String)->()) {
    
    print("fetchData - 진입 ")
    
    DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
        
        print("fetchData - data  \(data) ")
        
        completionHandler("\(data)")
    }
    
    print("fetchData - 종료 ")

}


let group = DispatchGroup()
var text = ""
for i in 0..<20 {
    group.enter()
    fetchData(i, delay: Double.random(in: 0...0.2)) {
        
        
        
        text += "\($0) - "
        group.leave()
    }
}

group.notify(queue: DispatchQueue.main) {
    print(text)
    exit(0)
}

 

해결 - 2

DispatchSemaphore 사용

 

각 fetchData 함수는 이전에 실행되었던 작업이 완료될 때까지 기다린다.

출력은 순서대로 출력이 되고 결과는 아래와 같다.

 

0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 -

 

import Foundation


// 지연 후에 Int를 String으로 변환하는 함수
func fetchData(_ data: Int, delay: Double, completionHandler: @escaping (String)->()) {
    
    print("fetchData - 진입 ")
    
    DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
        
        print("fetchData - data  \(data) ")
        
        completionHandler("\(data)")
    }
    
    print("fetchData - 종료 ")

}


DispatchQueue.global().async {
    let semaphore = DispatchSemaphore(value: 0)
    var text = ""
    for i in 0..<20 {
        fetchData(i, delay: Double.random(in: 0...0.2)) {
            text += "\($0) - "
            semaphore.signal()
        }
        semaphore.wait()
    }
    print(text)
    exit(0)
}

 

 

요약하자면, 비동기 기능을 동기화하기 위한 두 가지 도구가 있다.

모든 작업이 완료될 때까지 기다려야된다면 DispatchGroup을 사용하고, 함수를 호출하는 순서대로 완료하고 실행하려면 DispatchSemahpore 사용하면 된다.

 

 

 

참고

https://betterprogramming.pub/synchronizing-async-code-with-dispatchgroup-dispatchsemaphore-de814e485e82