Tong's Blog

[iOS] GCD(Grand Central Dispatch)란? 본문

iOS/Swift

[iOS] GCD(Grand Central Dispatch)란?

통스 2020. 5. 6. 13:18
반응형

안녕하세요.

 

오늘은 iOS에서 중요한 개념 중 하나인 GCD에 대해 알아보겠습니다.

 

GCD에 대해서 알려면 우선 Disapatch Queue에 대해 알아야하는데요. 우선은 역시 공식문서를 살펴보겠습니다.

https://developer.apple.com/documentation/dispatch/dispatchqueue

 

DispatchQueue - Dispatch | Apple Developer Documentation

Dispatch queues are FIFO queues to which your application can submit tasks in the form of block objects. Dispatch queues execute tasks either serially or concurrently. Work submitted to dispatch queues executes on a pool of threads managed by the system. E

developer.apple.com

(늘 영어의 한계가...)

 

간단히 보니 앱의 메인스레드와 백그라운드 스레드에서의 작업을 직렬(serially) 혹은 동시적(concurrently)으로 관리해주는 큐(Queue)라고 합니다. 그리고 중요표시로 동기적인 실행( synchronously execute )는 교착상태(deadlock)을 불러일으킬 수 있으니 주의하라고 합니다.

 

자 우선 Dispatch Queue에는 두가지 관리방식이 있다고 했죠?

직렬과 동시 방식의 큐가 존재하고 각 큐에는 또 동기(sync)와 비동기(async)의 방식이 존재하기 때문에 총 4가지 형태의 조합이 나타날 수 있습니다. 동기 방식은 큐에 포함된 작업들이 끝나야 그 다음 행이나 작업들을 실행하는 것이고 비동기 방식은 큐에 포함된 작업들을 시작하기만 하고 종료 순서를 보장하지 않습니다.

 

  1. 직렬.동기
  2. 직렬.비동기
  3. 동시.동기
  4. 동시.비동기

직렬(Serial)

 

직렬큐는 큐에 추가된 작업들이 각 순서에 맞게 시작되고 순서에 해당하는 작업이 끝나야만 그 다음에 작업을 시작하는 큐입니다.

 

  • 직렬.동기

직렬 동기 방식은 큐에 내부에 있는 작업들의 시작 및 종료 순서가 보장되고 해당 큐안의 작업이 모두 끝나야 큐 밖에 작업을 실행할 수 있을 겁니다. 해당 방식은 일반적으로 우리가 일반 함수를 사용하는 방식이겠네요.

 

  • 직렬.비동기

직렬 비동기 방식은 큐 내부에 작업들의 순서는 보장되겠지만 큐 밖에 있는 작업들과의 순서가 보장되지 않습니다. 한번 예시를 들어볼까요?

var temp:Int = 0

DispatchQueue(label: "Tong").async {
    for _ in 0..<10{
        temp = temp + 1
    }
}
print("Temp : " ,temp + 1)

temp = 0

DispatchQueue(label: "Tong").sync {
    for _ in 0..<10{
        temp = temp + 1
    }
}
print("Temp : " ,temp + 1)

해당 코드를 실행한 결과는 다음과 같습니다.

Temp :  1
Temp :  11

 

차이점이 느껴지시나요?

 

우리가 일반적인 생각이라면 for문을 모두 돌고 나서 temp가 10인 된 상태에서 temp+1을 호출하면 11이 나와야 할것같지만 async는 해당 순서를 보장하지 않기 때문에 for문에 코드보다 print가 먼저 호출되어서 0+1 = 1인 결과값을 얻게 된 것입니다.

 

sync는 for문을 모두 돌고 나서야 하단에 print가 호출되기 때문에 우리가 예상한 11을 얻을 수 있었습니다.

 

동시(Concurrnet)

 

동시(병렬)큐는 큐에 추가된 작업들이 각 순서에 맞게 작업이 시작되는 것은 직렬큐와 동일하지만 직렬큐와 다르게 앞 순서의 작업이 끝나야 실행되는 것이 아닌 실행 중간에라도 새 작업이 시작되고 동시에 여러 작업이 실행중인 상태가 될 수 있습니다.

 

우선 그렇다면 동시성인 경우 어떻게 나타날지 예상해보고 코드를 한번 보죠

var temp:Int = 0

DispatchQueue.global().async {
    for _ in 0..<10{
        temp = temp + 1
    }
}
print("Temp : " ,temp + 1)
temp = 0

DispatchQueue.global().sync {
    for _ in 0..<10{
        temp = temp + 1
    }
}

자 이번엔 어떤 결과를 얻을 수 있을까요?

동시큐는 직렬큐와 다르게 나타날까요? 우선 결과값을 먼저 보시죠

Temp :  1
Temp :  11

위에 있는 코드와 똑같이 sync는 11, async는 1인 결과값을 얻을 수 있었네요?

그렇다면 직렬과 동시는 실제로 같은 결과가 나타날까요?

 

물론 아닙니다. 위 같은 경우는 연산이 적어서 우리가 보고 싶은 차이를 보기엔 너무 빠르게 지나가기에 직렬과 같은 결과값을 얻을 수 있었습니다. 그럼 연산 수를 좀더 늘려보겠습니다.

 

var temp:Int = 0

DispatchQueue.global().async {
    for _ in 0..<1000{
        temp = temp + 1
    }
}
print("Temp : " ,temp + 1)
temp = 0

DispatchQueue.global().sync {
    for _ in 0..<1000{
        temp = temp + 1
    }
}

결과값부터 볼까요?

Temp :  1
Temp :  991

자 차이점이 느껴지시나요?

두번째 결과값이 991은 매번 달라질 수 있습니다.

 

해당 코드를 동시큐가 아닌 직렬큐를 사용하면 우리가 생각한 결과값인 1001을 얻을 수 있었을겁니다.

 

하지만 동시큐에서는 큐 안에있는 연산들의 완료순서를 보장하지 않기에 다른 연산이 미처 끝나기 전에 다른 연산이 시작되서 temp변수에 정상적으로 값이 누적이 되지 않은것이죠.

 

오늘은 GCD와 DispatchQueue를 사용해 동기와 비동기, 직렬과 동시적인 프로그래밍 방식과 예제를 살펴봤습니다. 기회가 된다면 이런 방식을 우리가 코드 어느 부분에 적용하면 좋을지에 대해서 알아보는 시간이 있었으면 좋겠습니다.

 

읽어주셔서 감사합니다.

반응형
Comments