안녕하세요 린다입니다.
요즘 멋사 끝나고 헷갈리는 문법부터 보고 있는데요.
이번달 목표는 GCD를 마스터라서.. GCD를 정리해보았어요.
밑에서부터 말..편하게 해서.. 제가 이해한 바를 정리해보겠습니다.
본 정리는 앨런님의 강의를 수강하면서 이해하고 정리한 내용입니다!!
먼저.. 우리가 DispatchQueue에 대한 궁금증과 어려움이 생겼다면,
동시성에 대한 의문이 분명히 있으실텐데요...
이 동시성이 무엇인지부터 시작해서 정리해보겠습니다..!!!
동시성이 무엇이며, 도대체 왜 Concurrency 이 필요한가?
우리 컴퓨터의 발전에 따라서… 작업이 고도화 되고 처리할 양이 많아짐 == 일해야 하는 놈이 증가함
그래서 하나의 쓰레드가 아니라 다른 쓰레드로 일을 분산시켜서 작업을 빠르게 처리해보자에서 동시성 개념 탄생
즉, 동시성 : 작업을 다른 쓰레드에서 동시에 일하도록 만드는 것!
그럼 iOS에서 동시성을 어떻게 주입하느냐?
⇒ 대기행렬(Queue)에 보내면 운영체제가 알아서 다른 쓰레드로 보내줌 !
(Queue? : 선입선출로 동작하는 자료구조)
그렇다면.. iOS에서 동시성을 위해서, Queue에 보내는 방법은?
iOS’s Queue 2가지가 있음
- GCD (Grand General Dispatch)
- OperationQueue
⇒ Queue를 사용함으로써 다른 쓰레드에서 오래 걸리는 작업들이 비동기”적”으로 동작하도록 만들어줌
⇒ 우리는 대기열=Queue에 보내기만 하면 알아서 다른 쓰레드로 분산해서 비동기적으로 실행시켜줌
⇒ 즉, Queue를 사용한다는 것은 “다른 쓰레드로 = 동시성” 보내서 일을 처리하겠다는 말!!
*비동기 적!인 것이지 비동기라는 말은 아님!!! -> 비동기는 밑에서 더 자세하게 다루겠습니다.
*우리가 코드를 짜면서 사용하던 메인쓰레드에서(1번 쓰레드) 큐로 작업을 보내면 그 큐에서 다른 2,3…와 같은 쓰레드에 일을 보낸다는 것. 갑자기 냅다 Queue라는게 생성되는것이 X
Swift에서 코드로 GCD 사용법
GCD라는 개념, “큐”라는 것을 swift에서 사용하기 위해서는 DispatchQueue라는 API를 사용하면 됨
DispatchQueue.global().async {
//... 작업의 한 단위
}
DispatchQueue : 메인 제외 다른 쓰레드를 사용하기 위해서 대기열인 큐에 작업을 보낸다.
global() : 글로벌하게
async : 비동기적으로
{} : 작업의 단위
⇒ {}라는 작업을 여러쓰레드를 사용해서 처리하기 위해 대기열큐에
글로벌하게 비동기적(다른 쓰레드로 보낸 일의 완료 유무를 기다리지 않고)으로 보낸다는 코드
⇒ DispatchQueue.global() 에서 global()은.. 대기열인 큐에 보내는데, 어떤 큐의 우선순위를 설정하는것
그럼 여기서 aysnc, 비동기, 안 기다린다는 무엇인지?
Async == 비동기
- Async, 비동기 : 현재 코드를 짜면서 사용중이던 메인 쓰레드에서, 다른 쓰레드로 자신이 처리할 일을 보내고, 보낸 그 일의 완료 유무와 상관없이 바로 다음 일을 처리할 수 있는 것
- “기다리지 않는다” 가 포인트
- 메인말고 다른 쓰레드를 사용한다와는 완전 다른말임, 다른 쓰레드로 보낸 그 작업을 메인 쓰레드에서 완료되는지 안 되는지에 대한 것을 기다리지 않겠다는 말!!!!
Sync == 동기
- Sync, 동기: 현재 코드를 짜면서 사용중이던 메인 쓰레드에서, 다른 쓰레드로 자신이 처리할 일을 보내면, 그 일이 완료될 때까지 메인 쓰레드는 다른 작업을 못하고 기다려야함!
- “기다린다”가 포인트
- 그래서 DispatchQueue.global.sync를 해도.. 나는 이 작업을 다른 쓰레드로 보내서 일 처리를 할 건데.. 그 보낸 일 처리가 완료 될때까지 기다릴거야라는 말. 말, 다른 쓰레드로 보내도 보낸 쓰레드에서 일처리가 끝나야 메인쓰레드에서 다른 일처리를 할 수 있는 것이기 때문에 DispatchQueue를 사용하는 의미가 없어짐. 쓰나마나 똑같음
다시 이제 돌아와서.. 동시성을 더 자세하게 알아보자
우리가 “동시성”이라고 말하는 것은 사실은 병렬이라고 생각해야함.
엥?? 싶지만.. 다시 풀어보자면…
동시성의 의미는 그저 “다른 쓰레드를 사용하겠다” 라는 말
이제 여기서 “다른 쓰레드”의 개수로 또 나뉨
- 이중에서 다른 쓰레드 1개만 사용하는 것이 직렬, Serial이고,
🔺 다른 쓰레드가 2번 쓰레드인지, 3번 쓰레드인지는 모름! - 1개보다 더 많은(2개 이상) 다른 쓰레드를 사용하는 것이 동시성, Concurrent의 의미
🔺 몇개의 쓰레드로 분산할 지는 시스템이 알아서 결정함!!
즉, Serial은 다른 한개의 쓰레드에서, Concurrent는다른 여러개의 쓰레드에서!!
그럼.. 무조건 다른 여러개의 쓰레드를 사용하는 것이 좋은게 아니냐?? → 각 장점이 있음
- 직렬 : 순서가 중요한 작업을 처리할 때 사용함 (1개의 쓰레드니까 순차적일 수밖에 없음)
- 동시 : 각자 독립적이지만 유사한 여러개의 작업을 처리할 때 사용함
(예시.. 당근 마켓과 같은 같은 구성의 각 셀이 각자 독립적이지만, 유사한 여러개의 작업임)
⇒ 둘 다 “병렬”하게 메인 제외 다른 쓰레드를 사용하는게 맞지만.. 그 쓰레드의 개수에 따라서 직렬이나 동시냐로 나뉘는 것
그럼 이제,, DispatchQueue에 대해서 더 알아보자
위에서 global이 DispatchQueue(GCD) 종류 중에 하나라고 했음!
Queue라는 것은 GCD, Opeartaion으로 나뉘며, 더 세부적으로 GCD는 main, global, private으로 나뉨
- GCD라는 큐는 더 세부적인 main queue, global queue, private queue로 나뉘는 것이며, 각 대기열마다 특성이 다름
- 각 큐가 각자 알아서. 다른 쓰레드를 생성하고 일을 처리한다는 의미
1. main queue
- 유일한 쓰레드. 즉, 시리얼적으로 동작함 그래서 == 메인쓰레드임 (메인큐 == 메인쓰레드)
- 두개는 같은 의미적으로의 코드임
- print를 DispatchQueue.main.sync 로 둘러싸는 것과 안 싸는 것이 의미적으로 동일하는 말
print("print something")
DispatchQueue.main.sync {
print("print something")
}
2. global queue
- 기본 설정이 2개 이상의 쓰레드를 사용하는 concurrent 동시적이며, 총 6개의 종류(Qos)를 가지고 있음
- 즉, 순서가 중요하지 않은 작업을 보내는 것을 인지하고 사용해야함.
* Qos의 종류 (각 큐의 우선순위)
- userInteractive : 사용자와 상호 작용하는 유저와 직접적 인터랙티브
- userInitiated : 유저가 즉시 필요하긴 하지만, 비동기적으로 처리된 작업
- default : 일반적인 작업 (→ 잘 모르겠음 이거 하면댐)
- utility : ProgressView(Indicator)와 함께 길게 실행되는 작업 (몇초-몇분)
- background : 유저가 직접적으로 인지하지 않은 작업 (몇분 이상)
- unspecified : 레거시 API를 위한 Qos, 실제로 사용X
그럼 대체.. Qos의 의미가 무엇이냐, 여기서 설정하는 우선순위는 어떻게 어떤 의미로 사용되는 것인지?
TimeCheck {
DispatchQueue.global(qos: .userInitiated) {
// ... task1
}
DispatchQueue.global() {
// ... task2
}
}
를 예시로 정리해보자면…
먼저 첫번째 큐는 userInitiated 라는 Qos를 설정하였기 때문에 두번째 큐인 global보다 우선순위가 높음.
그렇다면… TimeCheck 함수를 실행시 무조건 첫번째 큐가 먼저 실행되고 먼저 끝나는가? ⇒ 아님. 아무도 모름
왜냐면, 우선순위가 높다는 게… global을 사용한다는 것은 이미 2개 이상의 쓰레드를 사용한다는 거에 더해
여기서 우선순위를 주는 것이기 때문에, 사용하는 쓰레드의 개수가 많아진다는 것을 의미함.
(그치만 사용하는 쓰레드의 개수가 많아진다고 하더라도 무조건적으로 해당 일이 먼저 끝나는지 아닌지는 보장 못함)
즉, 운영체제에서 userInitiated큐는 default큐보다 우선적으로 쓰레드가
사용되어야 하는 작업임을 인지하고 default 작업보다 더 많은 쓰레드를 사용해서 일을 처리하려고 함,
더 여러개의 쓰레드를 배치하고 배터리를 집중해서 사용하게끔 만든다는 것
그래서.. 우선순위를 높은 걸 사용했다고 해서 꼭! 그 작업이 먼저 실행되고 먼저 마무리되는지는 모른다는 것!!!
요 Qos 가 들어오는 순간.. 이제 비동기 동기와 겹쳐서 헷갈리게 되는데요...
예시를 보면 딱 이해갈거라고 생각함니다...
먼저 실행할 작업을 이렇게 정의해두고
이렇게 싱크, 어싱크, qos를 섞어서 실행하면 결과는..
첫번째이며 싱크로 실행되는 task1을 제외하고는 결과가 랜덤으로 출력됩니다.
세 코드 모두 동일한 코드임에도 첫번째를 제외하고는 다 다른 순서로 실행/완료 되는데요
편하게 정리해보자면..
*DispatchQueue 자체가 원래 동시적을 내포하고 있어서, GCD를 사용하면 여러 쓰레드를 사용할 수 있음
첫번째 task1의 sync 이거는 첫번째 작업이자 "동기"적인 작업이기 때문에 큐에 올리고,
그 일을 쓰레드에서 처리하는 동안 완료될때까지 기다려야함
-> 그래서 얘는 무조건 첫번째 처리 + 완료될때까지 다른 코드들이 실행 안됨
두, 세번째의 task2와 task4는 async, 이거는 비동기이며 다른 우선순위..
즉, 두개는 완료될때까지 메인 쓰레드에서 기다리지 않는데 두개 다 다른 쓰레드로 보내지게 됨, 언제 완료되는지 모름
마지막 작업의 task3의 sync 얘도 task2, task4를 거쳐서 바로 다른 메인이
아닌 다른 쓰레드의 동기 작업으로 올라감 이렇게 되어버리니까
=> 1번째를 제외한 나머지는 다 각자 다른 쓰레드로 올라가서 어느게 먼저 실행되고 완료되는지 알 수 없기에 랜덤 결과 출력
3. private queue (잘.. 사용 안 하는 ..듯..)
- 프라이빗(커스텀) 큐
- 커스텀으로 직접 만들어서 사용하는 큐이며, 디폴트는 Serial으로 설정되어 있음 (물론, concurrent(동시)로 설정 가능함)
- Qos 설정 가능 → 비교적 운영체제에서 알아서 판단해줌
// 직렬로 커스텀 큐를 생성하는 것
let queue = DispatchQueue(label: "Linda")
// 동시적으로 커스텀 큐를 생성하는 것
let queue = DispatchQueue(label: "Linda", attributes: .concurrent)
동기/비동기, 그리고 동시를 잘 구분하고 이해해야 하는것 같아요!
일단 GCD는 여기까지 1차 마무리해보고 추가되는 내용이 있다면 두번째 게시물로 추가하겠습니다 !!
앨런 덕분에.. 속 시원하게 이해 완료하고 정리해보았습니다...
오늘의 결론.. 앨런 최고 . .. .~~!
'Swift > Swift 기초문법' 카테고리의 다른 글
Swift 기초 문법, 함수와 클로저를 톺아보자 (1) | 2024.02.06 |
---|---|
DispatchQueue(GCD)를 알아보자2, GCD 사용시 주의해야할 사항 (0) | 2023.11.08 |
[Swift 기초문법] Error Handling을 알아보자 (0) | 2023.07.17 |
[Swift 기초문법] @State, @Binding 을 알아보자 (0) | 2023.07.10 |
[Swift 기초문법] Generics을 알아보자 (0) | 2023.07.10 |