GCD
- Grand Central Dispatch
 
- 모든 애플 플랫폼에서 사용 가능
 
- 스레드 생성, 관리
 
- 스레드 풀을 통해 스레드 재사용, 시스템 리소스를 적게 사용
 
Dispatch Queue & Work Item
- GCD의 핵심 개체
 
Work Item: 실행할 코드 
Dispatch Queue: 작업을 관리하는 객체 
- Dispatch Queue에 Work Item을 추가하면 추가한 순서대로 작업이 실행된다. 이러한 모델을 
Work-Queue Programming Model이라고 한다. 
Serial Queue & Concurrent Queue
- Dispatch Queue의 작업 방식
 
Serial Queue: 추가된 작업을 순서대로 실행(기본) 
Concurrent Queue: 동시에 작업을 실행(concurrent 옵션으로 생성) 
Global Queue & Main Queue
DispatchQueue.global().async{ 
            for num in 1 ... 100{
                
                DispatchQueue.main.async{ 
                    self.countLabel.text = "\(num)"
                }
                Thread.sleep(forTimeInterval: 0.1)
            }
        }
global : 백그라운드 스레드에서 동작하는 기본 큐 
main : 메인스레드에서 동작하는 기본 큐
- 메인 큐는 앱 시작 시점에 자동으로 생성
 
- serial 함
 
- UI에 관련된 작업은 반드시 main에서 실행
 
 
Dispatch Queue 생성

let serialQueue = DispatchQueue(label: "SerialQueue") 
let concurrentQueue = DispatchQueue(label: "ConcurrentQueue", attributes: .concurrent) 
Work Item 생성
sync & async
    @IBAction func sync(_ sender: Any) {
        concurrentQueue.sync {
            for _ in 0 ..< 3 {
                print("Hello")
            }
            
            print("# Point 1")
        }
        
        print("# Point 2")
    }
    @IBAction func async(_ sender: Any) {
        concurrentQueue.async {
            for _ in 0 ..< 3 {
                print("Hello")
            }
            
            print("# Point 1")
        }
        
        print("# Point 2")
    }
- sync, async는 실행하는 메소드가 아닌, 큐에 work item을 추가하는 메소드다.
 
- 실제 작업 실행은 dispatchQueue
 
- async와 sync가 dispatchQueue의 동작 방식에는 영향을 주지 않는다. 
- serial Queue에 추가할 때 둘 중 어느것을 사용하던 항상 작업을 순서대로 실행한다.
 
- concurrent Queue에 추가할 때 둘 중 어느것을 사용하던 동시에 작업을 실행한다.
 
 
sync 결과

- sync는 동기 방식으로 work를 추가
 
- work아이템을 추가한 다음 바로 리턴하지 않고 실행이 끝날 때까지 기다린다.
 
- 주로 lock과 유사한 동기화를 구현할 때 사용
 
- mainQueue에서 sync를 사용하면 크래시가 발생하므로 주의하자.
 
async 결과

- async는 비동기 방식으로 work를 추가
 
- work아이템을 추가한 다음 바로 리턴하므로 이어지는 코드가 바로 실행된다.
 
- 스레드를 블로킹하지 않으므로 대부분은 async로 work 아이템을 추가한다.
 
asyncAfter로 실행 시간 지연
@IBAction func delay(_ sender: Any) {
        let delay = DispatchTime.now()  + 10
        
        concurrentQueue.asyncAfter(deadline: delay) {
            print("# Point 1")
        }
        
        print("# Point 2")
    }

Point 2 바로 출력 후 10초 뒤에 Point 1 출력
asyncAfter은 비동기 메소드 
- 지정한 시간에 작업을 예약하고 바로 리턴
 
병렬 실행
DispatchQueue.concurrentPerform(iterations: , execute: (Int) -> Void)
- execute 블럭을 iterations만큼의 횟수로 병렬 실행
 
    @IBAction func concurrent(_ sender: Any) {
        
        var start = Date.now
        
        for index in 0 ..< 50 {
            print(index, terminator: " ")
            
            Thread.sleep(forTimeInterval: 0.1)
        }
        
        print()
        
        var end = Date.now
        
        print("serial: \(end.timeIntervalSinceReferenceDate - start.timeIntervalSinceReferenceDate)")
        
        start = Date.now
        
		
        DispatchQueue.concurrentPerform(iterations: 50) { index in
            print(index, terminator: " ")
            
            Thread.sleep(forTimeInterval: 0.1)
        }
        print()
        
        end = Date.now
        
        print("serial: \(end.timeIntervalSinceReferenceDate - start.timeIntervalSinceReferenceDate)")
    }

- 보통 병렬로 실행할 수 있는 작업의 수는 CPU의 코어 수와 동일