GCD

Serial vs Concurrent

  • 은행을 예로 들자면, Serial의 경우는 한 명의 은행원이 있고, 한 줄의 대기라인이 존재한다고 볼 수 있다.
  • 반면 Concurrent는 한 명의 은행원은 같지만, 한 줄이 아닌 여러줄의 대기라인이 존재한다.
  • Serial의 경우 번호표를 뽑고 기다리면, 번호 순서대로 부른다.
  • Concurrent의 경우에는 순서대로 번호표를 주지만, 반드시 번호순대로 불리진 않는다. (중요도가 존재하기 때문)

Sync vs Async

  • Sync와 Async의 차이는 현재 코드가 실행 완료되기 전까지는 다음 라인을 실행하지 않느냐 마느냐 하는 것이다.
  • Sync는 큐에 추가한 작업이 끝나기 전까지 다음 코드를 실행하지 않는다.

Serial - Sync

func test() {
    let serialQueue = DispatchQueue(label: "myQ")
    
    serialQueue.sync {
        for i in 0...100 {
            print(i)
        }
        print("a")
    }
    print("b")
}

// 결과

0
1
2
.
.
.
100
a
b
  • 우선 위 코드는 Serial Queue이기 때문에 한 줄로 실행코드들이 대기한다. (for-in문, print(“a”) 순서)
  • 그리고 Sync이기 때문에 Queue에 추가된 for-in문과 print(“a”)가 끝날때까지 print(“b”)가 실행되지 않는다.

Serial - Async

func test() {
    let serialQueue = DispatchQueue(label: "myQ")
    
    serialQueue.async {
        for i in 0...100 {
            print(i)
        }
        print("a")
    }
    print("b")
}

// 결과

b
0
1
2
.
.
.
100
a
  • 위 코드도 Serial Queue이기 때문에 한 줄로 실행코드들이 대기한다. (for-in문, print(“a”) 순서)
  • 그리고 Async이기 때문에 Queue에 추가된 for-in문, print(“a”)를 기다리지 않는다.

Concurrent - Sync

func test() {
    DispatchQueue.global().sync {
        for i in 0...100 {
            print(i)
        }
        print("a")
    }
    print("b")
}

// 결과

0
1
2
.
.
.
100
a
b
  • 위 코드는 우선 ConcurrentQueue이다. 즉 여러 줄을 만든다. 이때 줄을 결정하는 것은 별도의 중요도를 지정하지 않으면 시스템이 알아서 배치한다.
  • Sync이기 때문에 Queue에 등록된 for-in문과 print(“a”)가 끝날때까지 print(“b”)는 실행되지 않는다.

Concurrent - Async

func test() {
    DispatchQueue.global().async {
        for i in 0...100 {
            print(i)
        }
        print("a")
    }
    print("b")
}

// 결과

0
b
1
2
.
.
.
100
a
  • 위 코드는 마찬가지로 Concurrent Queue라서 줄을 멋대로 선다.
  • 또 Async 이므로 Queue에 등록된 코드가 끝날때까지 기다리지 않는다.

Concurrent - Async QoS 지정

func test() {
    DispatchQueue.global().async {
        DispatchQueue.global(qos: .background).async {
            for i in 0...100 {
                print(i)
            }
        }
        DispatchQueue.global(qos: .userInteractive).async {
            print("a")
        }
    }
    print("b")
}

// 결과

a
b
0
1
.
.
.
100
  • 원래의 경우라면 마음대로 줄을 서야할 concurrent이지만, print(“a”)의 경우 userInteractive 즉 최우선 중요도로 지정했고, for-in문은 최하로 지정했다.
  • 또 Async이기 때문에 Queue에 등록된 코드 실행 완료를 보장하지 않고 print(“b”)가 실행된다.

DispatchGroup

  • DispatchGroup을 사용해서 해당 Queue 그룹을 만들고 관리할 수 있다.
// 커스텀 그룹 생성
let customGroup = DispatchGroup()

// Serial Queue 생성
let q1 = DispatchQueue(label: "customQueue")
let q2 = DispatchQueue(label: "customQueue")

// customGroup에 진입
customGroup.enter()
q1.async {
    print("q1 done")
    customGroup.leave() // customGroup에서 이탈
}

customGroup.enter()
q2.asyncAfter(deadline: .now() + 5) {
    print("q2 done")
    customGroup.leave()
}

// parameter queue는 무슨 역할인지 모르겠음. DispatchQueue.main, DispatchQueue.global(), CustomDispatchQueue 등
// 다양한 Queue를 넣어봐도 아무것도 달라지지 않음.
// 애플 문서에도 설명도 없고 쓰이지 않는 parameter 인듯...
customGroup.notify(queue: q1) {
    print("customGroup done.")
}

// 결과
q1 done
q2 done
customGroup done.