GGURUPiOS
동시성 프로그래밍 - GCD (1) 본문
GCD (Grand central Dispatch)
GCD는 동시성 환경에서 작업을 실행하기 위한 API를 제공함
시스템 수준에서 스레드 및 대기열 관리를 처리함
비동기 실행되는 작업을 쉽게 처리할 수 있고 작업 간의 의존성이나 순서를 지정할 수 있음
DispatchQueue는 GCD를 사용하기 위한 대기열로, 대기열에 작업을 추가해서 작업을 처리하도록 도와줄 것임 (First In, FirstOut)
DispatchQueue에 작업을 넘길 때는 2가지를 정해주어야 함
- 단일, 다중 스레드 여부 ( Serial / Concurrent )
- 동기, 비동기 여부 ( sync / async )
DispatchQueue가 GCD와 같은 개념은 아니고, GCD가 더 넓은 개념임
GCD는 Dispatch라는 프레임워크와 같은 개념이라 볼 수 있음
Dispatch 프레임워크에는 DispatchQueue, DispatchWorkItem, DispatchGroup, DispatchQos 등 그 외에도 다양한 타입들이 구현되어 있음
DispatchQueue 를 자세히 알아보자
Serial / Concurrent
Serial 은 단일 스레드에서만 작업
Concurrent는 다중 스레드에서 작업
DispatchQueue 를 초기화할때 attributes를 따로 .concurrent로 지정하지 않으면 기본값은 Serial임
// Serial Queue
DispatchQueue(label: "Serial")
DispatchQueue.main
// Concurrent Queue
DispatchQueue(label: "Concurrent", attributes: .concurrent)
DispatchQueue.global()
global이 메서드인 이유
→ Main Thread는 메모리에 늘 올라와 있는 기본 스레드임
→ 하지만 Concurrent Queue는 Main Thread외의 새로운 스레드를 만들기 때문에 메서드가 됨
main / global
main과 global은 이미 만들어져있는 큐(대기열)로 main - Serial, global - Concurrent 큐임
main은 전역적으로 사용 가능한 큐 임 (늘 메모리에 있음) (메인 스레드)
main은 serial 큐이기 때문에 동시에 여러 작업을 처리할 수 없음
global에 작업을 추가하면 새로운 스레드를 만들어 그 위에서 작업을 처리함
global 스레드는 메모리에 올라왔다가, 작업이 끝나면 메모리에서 제거됨
Main Thread 란?
메인 스레드는 앱의 기본이 되는 스레드 ( 앱이 실행되는 동안에 늘 메모리에 존재 )
여러개의 스레드를 사용할 때도, 메인 스레드에서부터 필요한 만큼의 스레드가 파생되는 것임
메인 스레드 특징
- 전역적인 사용 가능
- global 스레드와 다르게 Run Loop 가 자동 설정, 실행
- UI 작업은 메인 스레드에서만 가능
위에서 말한
- 단일, 다중 스레드 여부 ( Serial / Concurrent )
- 동기, 비동기 여부 ( sync / async )
중 단일, 다중 스레드 여부를 정했으면 동기, 비동기 여부를 정해줘야 함
// 동기
DispatchQueue.main.sync {}
DispatchQueue.global().sync {}
// 비동기
DispatchQueue.main.async {}
DispatchQueue.global().async {}
위와 같이 작성해 줄 수 있음
sync / async
sync는 동기로 작업한다는 의미. 코드 블럭이 하나의 작업이 됨
async는 비동기로 작업한다는 의미
main.async
만약 위와 같이 코드를 작성했다면 비동기이기 때문에 a b a a b a 이런식으로 출력될 것 같지만, 실제는 a a a a a b b b b b 이런식으로 출력이 됨
→ 단일 스레드에서만 작업이 이루어지기 때문에 DispatchQueue에 쌓인 순서대로 작업이 처리되는 것임
만약 위와 같이 실행하면 어떻게 될까?
b b b b b 가 먼저 출력 될 것 같지만
c,c,c,c,c 가 먼저 출력되곤 함 ( 정확히는 어떤 것이 먼저 출력될지 모름 )
→ 단일 스레드 환경이지만 비동기로 코드 블럭을 호출했기 때문에, 다음 코드로 넘어감
→ 다음 코드가 먼저 실행 되면 c를 호출하는 코드가 먼저 출력 되는 것임
global().async
메인 스레드가 아닌 다른 스레드를 만들어 비동기로 처리하도록 함
여기서 출력이 된 결과를 보면 랜덤하게 ( a b a b b b a a …) 이런식으로 출력이 됨
→ main.async 와는 다르게 새로운 스레드를 생성하기 때문에 ( 위에서는 2개 생성 됨 ) 동시에 작업들이 처리 됨
→ 하지만 어떤 코드가 먼저 실행될지는 알 수 없음 ( async 의 특성 )
global().sync
위와 같이 작성하면 sync 는 동기선언 이기 때문에 a a a a a b b b b b 이런식으로 출력이 된다
두개의 코드블럭은 스레드는 각각 다르지만 동기적으로 일을 처리하기 때문에
각 작업이 끝나기를 기다림
main.sync
main.sync는 직접 호출하면 안되는 코드임
직접적으로 호출하면 deadlock 에 빠지게 됨
sync 는 코드 블럭이 처리되기 전까지 다음 코드로 넘어가지 않음 ( Block-wait )
메인 스레드에서 main.sync 를 호출하게 되면 메인 스레드는 sync의 코드 블럭이 수행되기를 기다려야함
하지만 이 때 sync의 코드 블럭 역시 메인스레드에서 동작하기 때문에 멈춰버림
메인 스레드는 sync가 끝나기를 , sync는 메인스레드의 Block-wait이 끝나기를 기다림
그러나 호출 할 수 있는 경우도 있는데 메인스레드가 아닌 다른 스레드에서 호출하는 경우임
예를들어 global 스레드에서 main.sync를 호출한다면 가능함
DispatchWorkItem
DispatchQueue에서 코드 블럭을 호출할 때, DispatchWorkItem을 활용해서 코드 블럭을 캡슐화 할 수 있음
위의 코드처럼 DispatchWorkItem 으로 묶어서 캡슐화가 가능하다
asyncAfter
asyncAfter는 async 메서드를 원하는 시간에 호출해줄 수 있는 메서드임
DispatchQueue.global().asyncAfter(deadline: .now() + 5, execute: apple
위의 코드는 지금 부터 5초 후에 apple 을 실행 시킨다는 의미임
파라미터의 두 종류 ( deadline, wallDeadline ) 가 있음
deadline : 스톱워치 느낌
wallDeadline : 시스템(기기)의 시간 기준
두 종류로 나눌 수 있으나, 코드 작성법과 동작은 거의 비슷함
asyncAndWait
asyncAndWait 메서드를 사용하면 비동기 작업이 끝나는 시점을 기다릴 수 있음
동작 논리는 사실 sync와 유사함
'Swift > 동시성 프로그래밍' 카테고리의 다른 글
동시성 프로그래밍 - Concurrency (2) Structured Concurrency (async-let, Task) (0) | 2023.04.24 |
---|---|
동시성 프로그래밍 - Concurrency (1) async/await (0) | 2023.04.24 |
동시성 프로그래밍 - Operation (0) | 2023.04.24 |
동시성 프로그래밍 - GCD (2) (0) | 2023.04.24 |
동시성 프로그래밍 - 동시성 프로그래밍이란? (0) | 2023.04.19 |