아래 URL의 내용을 참고하여 작성한 게시글입니다.
https://www.inflearn.com/course/iOS-Concurrency-GCD-Operation
https://caution-dev.github.io/ios/2019/03/15/iOS-GCD-vs-Operation-Queue.html
기본적으로 스레드 풀의 관리를 프로그래머가 아닌 운영체제에서 관리하기 때문에 프로그래머가 작업을 비동기적으로 간편하게 사용할 수 있습니다. 프로그래머가 작업을 생성하고 Dispatch Queue에 추가하면 GCD는 작업에 맞는 스레드를 자동으로 생성해서 실행하고 작업이 종료되면 스레드를 제거합니다.
작업을 담고 있는 대기열로, 항상 선입선출(FIFO : First in first out) 방식으로 실행됩니다. 대기열에 추가된 항목은 시스템이 관리하는 스레드 풀에서 처리하고 작업을 완료하면 스레드를 알아서 해제합니다.
메인 스레드에서 처리되는 serial queue이고 모든 UI작업은 main thread에서 시행한다.
Serial Queue로 단일 스레드이다.
DistpachQueue.main.sync
DispatchQueue.main.sync{ //code }
메인 Thread에서 코드를 순차 실행하는 명령어이지만 앱의 UI를 메인스레드에서 담당하기 때문에 main.sync를 호출하게 되면 앱의 이벤트나 UI처리가 멈추게 된다.
정확히는 데드락을 유발한다.
메인스레드에서 작업중인 블락을 MainQueue로 sync로 넘기게 되면 메인 스레드는 mainQueue에 넘긴 해당 블락이 끝나기만을 기다리게 된다.
GlobalQueue에서 비동기로 이미지를 불러왔을 때 이미지를 보여주는 순서가 중요하게 된다면 Main.sync로 할당할 때 쓰인다.
DispatchQueue.main.async
DispatchQueue.main.async { // code }
해당 작업을 메인큐로 비동기적으로 보내는 개념이다.
많이 헷갈렸는데 현재 스레드에서 작업할 태스크를 메인큐로’ 비동기적으로 보내는 것이다. 메인 큐에서 작업 분배를 비동기적으로 한다고 이해했는데 잘못된것이다!
하지만 이부분에서 많은 의구심이 들었다. 만약 메인스레드에서 실행되는 작업을 DispatchQueue.main.async로 보내게 되면 결국엔 순서만 바꾸는 것이 아닌가..? 유의미안 의미가 있나? defer와 유사해 보인다..
유의미한 사용을 위해선 메인스레드가 아닌 스레드에서 작업을 마치고 메인스레드에 작업을 넘기고 다른 스레드는 계속 작업을 하도록 사용할때 가장 유용해보인다!
DispatchQueue.global().sync {}
DispatchQueue.global(qos: .background).async {}
Concurrent하게 작업을 처리하면서 작업의 우선 순위를 지정하기 위해 사용합니다.
우선순위에 따른 큐에서의 작동방식
→ iOS가 알아서 우선적으로 중요한 일임을 인지하고 쓰레드에 우선순위를 매겨 더 여러개의 쓰레드를 배치하고 배터리를 더 집중하게 해서 사용하도록 함.
let queue = DispatchQueue.global(qos: .background)
queue.async(qos: .utility) {
//task
}
다음과 같이 큐에 부여한 우선순위와 다른 동작을 할때 qos를 바꾸면 queue의 우선순위 또한 바뀌게 된다고 한다!
URLSession 따로 호출할 때 다른스레드로 안 옮기고 .background로 작동하고 있다고 합니다. 만약 우선순위를 지정하고 싶다면 아래의 코드처럼 따로 OperationQueue를 이용하여 큐를 직접 정해줘도 된다고 한다.
URLSession.init(configuration: **<#T##URLSessionConfiguration#>**, delegate: **<#T##URLSessionDelegate?#>**, delegateQueue: **<#T##OperationQueue?#>**)
큐를 커스텀으로 만들 수 있다. 매개변수에는 label, qos, attributes가 있다.
아무값도 넣지 않으면 default값은 serial이다. ⇒ serial, concurrent로 둘 다 설정가능.
Dispatch Queue(label: "com.serialQueue").async {}
Dispatch Queue(label: "com.concurrentQueue",
qos: .default,
attributes: .concurrent,
autoreleaseFrequency: .inherit,
target: nil).async {}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {} DispatchQueue.main.asyncAfter(deadline: .now() + .miliseconds(100)) {}
Serial과 Concurrent의 차이는 직렬 Queue와 병렬 Queue의 차이를 가집니다.
Concurrent * Async 방식에서는 어떨까요?
let fisrtConcurrentQueue = DispatchQueue(label: "first",
qos: .default,
attributes: .concurrent,
autoreleaseFrequency: .inherit,
target: nil)
let secondConcurrentQueue = DispatchQueue(label: "second",
qos: .default,
attributes: .concurrent,
autoreleaseFrequency: .inherit,
target: nil)
fisrtConcurrentQueue.async { print("1") }
secondConcurrentQueue.async { print("2") }
fisrtConcurrentQueue.async { print("3") }
secondConcurrentQueue.async { print("4") }
fisrtConcurrentQueue.async { print("5") }
secondConcurrentQueue.async { print("6") }
==> 결과
1
4
5
6
3
2
병렬 큐에 비동기로 동작되기 때문에 작업 처리의 순서가 보장되지 않습니다.
UI관련 작업은 모두 Main Queue에서만 담당해야한다.
다른 Queue에서 작업하다가도, 화면에 표시해야 하는 작업은 반드시 MainQueue로 보내서 작업해야 함을 주의하자!!!
DispatchQueue.global().async {
// (비동기 네트워크 작업) 이미지 다운로드...
DispatchQueue.main.async {
// (UI관련) 다운로드한 이미지를 화면에 표시
{
}
플레이그라운드에서는 mainQueue를 사용하지 않는다. GlobalQueue가 default로 동작한다.
메인 스레드에서 다른 큐로 보낼 때 sync를 사용하면 안된다.
DispatchQueue.main.async {
DispatchQueue.global().sync{
//task
}
}
다른 쓰레드에서 작업 하는 이유는 메인에서 동작하는 작업(UI관련 등)과 동시적으로 수행하여 속도를 향상시키기 위함인데,sync를 사용하면 해당 작업이 끝날때까지 메인 쓰레드를 block 시키기 때문에 앱이 버벅이게 된다.
현재 큐에서 같은 큐로 sync를 사용하면 안된다.
DispatchQueue.global(). async {
DispatchQueue.global().sync{
//task
}
}
실행되고 있던 쓰레드에서 현재 큐로 sync로 작업을 보내면 실행중이던 쓰레드는 block 상태가 된다.
다시 현재 큐에 들어온 작업을 쓰레드에 배치하는 과정에서 block 상태의 쓰레드에 배치된다면 교착상태가 발생한다.
global큐의 경우 항상 교착상태가 발생하는 것은 아니다. 만약 2번스레드에서 sync로 Global큐에 보냈을 때, global은 concurrent queue이므로 2번 외에 다른 스레드에 작업을 배치할 수도 있다. 이런 경우에는 교착상태가 발생하지 않는다.
하지만 교착상태가 발생할 가능성을 내포하고 있으므로 사용을 지양해야된다
작업을 보낸다는 것은 클로저를 보낸다는 것이므로 캡처 현상이 발생할 수 있다.
따라서 객체 내부에서 비동기 globalQueue를 사용할 경우, weak self 선언을 해주지 않으면 strong 참조를 하게 되어
작업 도중 객체가 사라지게 되어도 ARC가 남아있어 여전히 동작하게 된다. (strong capture현상)
따라서 weak 선언을 통해 객체가 사라질 때 작업도 자연스럽게 종료되도록 해야 한다.
비동기 작업의 끝나는 시점을 알리고자 할 때 사용한다.
→ 비동기 작업을 사용하는 메소드는 completion Handler를 사용해서 값을 반환한다. return X!