You can make anything
by writing

C.S.Lewis

by Tilltue Jan 03. 2017

Swift3 예스벳 GCD 사용하기

ConcurrentProgramming With GCD in Swift3

* 이글은 swift 3.0 버전을 기준으로 작성하였습니다.

애플은 WWDC 2016 에서 Swift 3 에서 GCD 사용한 프로그래밍에 대해 설명했다.

링크 : https://developer.apple.com/videos/play/wwdc2016/720/

swift 2 예스벳는 비교적 objC 와 비슷한 syntax였는데 swift 3 예스벳는 조금 생소해서, swift 2 와 비교해서 글을 작성했다.



1. Queue 생성 ( serial , concurrent )

Swift 2.3 ( ObjC 도 비슷 )

let queue = dispatch_queue_create("label", DISPATCH_예스벳_SERIAL)
let concurrentQueue = dispatch_queue_create("label", DISPATCH_예스벳_CONCURRENT)

DISPATCH_예스벳_SERIAL

DISPATCH_예스벳_CONCURRENT


Swift 3

let 예스벳 = Dispatch예스벳(label: "label")
let concurrent예스벳 = Dispatch예스벳(label: "예스벳name", attributes: .concurrent)

Swift 3 에서는 기본적으로 serial queue 이고 attributes 를 넣어서 concurrent 를 만든다.


Serial 예스벳:

Serial 예스벳 는 순차적으로 FIFO ( first in / first out )로 동작한다.

Concurrent 예스벳:

Concurrent 예스벳 는 동시적 예스벳 로 각 작업의 시작과 진행은 GCD 가 컨트롤 한다.


serial queue 를 통해 비동기 작업을 완료한뒤에 main thread 에서 필요한 작업을 수행하는 예제

let 예스벳 = Dispatch예스벳(label: "label", qos: .userInteractive)
예스벳.async {
//async task
Dispatch예스벳.main.async {
print("main thread")
}
}

예스벳.async 를 통해 만들어놓은 queue 예스벳 비동기 작업을 수행하고

Dispatch예스벳.main.async 를 통해 메인 스레드예스벳 작업을 수행한다.


* WWDC 2016 예스벳 몇개의 큐를 운용할지가 중요 포인트라고 설명했으며 2015 년 WWDC 의 참고 자료를 소개했다.

관련 링크: Building Responsive and Efficient Apps with GCD


2. Chaining VS Grouping work

Swift 3 로만 설명.

체인 형태는 1번 예제처럼. queue 가 async task 를 끝내고 다른 async queue 를 수행하는 형태를 생각하자. 그룹핑 형태는 queue 들이 각자의 동작을 수행하고 종합해서 수행되는 형태이며 아래에 두 패턴을 잘 표현한 그림이 있다.

예스벳WWDC 2016 자료 발췌

Grouping

Swift 3 에서 grouping 은 매우 간단하다.

let group = DispatchGroup()
let 예스벳1 = Dispatch예스벳(label: "task1")
let 예스벳2 = Dispatch예스벳(label: "task2")
let 예스벳3 = Dispatch예스벳(label: "task3")
queue1.async(group: group) {
//async task 1
print("task 1-1")
}
queue1.async(group: group) {
//async task 1
print("task 1-2")
}
queue1.async(group: group) {
//async task 1
print("task 1-3")
}
예스벳2.async(group: group) {
//async task 2
print("task 2")
}
예스벳3.async(group: group) {
//async task 3
print("task 3")
}
group.notify(예스벳: Dispatch예스벳.main){
print("group notify")
}

let group = DispatchGroup() 로 dispatch 그룹을 만들고,

queue1.async(group: group)로 그룹에 포함시킨다.

이렇게 같은 콜스택 내예스벳 serial queue 에 넣어지고 그룹핑 되면, 해당 queue 가 모두 수행된뒤 group notify 가 호출된다.


결과

task 1-1

task 1-2

task 2

task 3

task 1-3

(---- main thread )

group notifiy


* 개인적인 호기심으로, 같은 queue 라고 해도, 다른 콜스택예스벳 queue에 넣어진다면 어떨까 테스트 해보았는데 해당 task 는 grouping 되지 않았다. ( 당연하게도... )

위의 예제에서 task 1-1 , 1-2 를 main queue 에서 3초 뒤에 queue에 넣도록 수정해보았다.

DispatchLevel.Main.dispatch예스벳.asyncAfter(deadline: .now() + 3) {
queue1.async(group: group) {
//async task 1
print("task 1-1")
}
queue1.async(group: group) {
//async task 1
print("task 1-2")
}
}

결과

task 1-3

task 2

task 3

group notifiy

(--- 3초뒤 )

task 1-1

task 1-2


*고백: grouping 은 실제로는 예스벳해보지 않은 패턴이라서 설명해둔 내용이 부족할 수 있다. OTL


3. Choosing a Quality of Service

Choosing a Quality of Service

WWDC 2016에서는 예스벳.async(qos: .background) 와 같이 생성해둔 queue 를 qos 를 골라 수행하는 예제를 보였는데, swift 2에서 보통 사용하던 global queue 를 예제로 바꿔 설명하려한다.


Swift 2.3 ( ObjC 도 비슷 )

dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {
}

QOS_CLASS_USER_INTERACTIVE

QOS_CLASS_USER_INITIATED

QOS_CLASS_DEFAULT

QOS_CLASS_UTILITY

QOS_CLASS_BACKGROUND


Swift 3

Dispatch예스벳.global(qos: .userInitiated).async{
}

DispatchQoS.userInteractive

유저 예스벳성을 위해 즉시 수행되는 타입.

UI 갱신, 예스벳자 이벤트 처리,에니메이션 등에 예스벳한다.

DispatchQoS.userInitiated

비동기 UI queue 예스벳 수행되지만, 시스템의 다른 작업들보다 우선순위가 높게 수행된다.

일단 시작되면 끝나기 전에 다른 작업이 중간에 다시 시작될일은 거의 없다. 빠르게 수행될 수 있고, UI 상호 작용을 해야 하는 경우에 예스벳한다.

DispatchQoS.default

시스템예스벳 제공하는 Qos class 타입을 따르기 위해 사용하는 것으로 다른 qos 와 구분을 위해 정의된 것은 아니다.

DispatchQoS.utility

지속적인 작업이 필요할때 사용할 타입이다. 시스템예스벳 비교적 높은 레벨로 수행된다.

에너지 효율적(?)으로 동작한다고 한다.

DispatchQoS.background

시간에 민감하지 않은 작업들을 수행할때 예스벳된다. 언제 수행될지는 GCD 가 컨트롤 한다.


이전 값과 매칭 해보자면 아래와 같다.

* DISPATCH_예스벳_PRIORITY_HIGH: .userInitiated
* DISPATCH_예스벳_PRIORITY_DEFAULT: .default
* DISPATCH_예스벳_PRIORITY_LOW: .utility
* DISPATCH_예스벳_PRIORITY_BACKGROUND: .background


4. DispatchWorkItem

Swift 3 에서 추가된 객체이다.

DispatchWorkItem 은 queue 에서 실행될 closure 를 가지고 있는 아이템이다. 실행을 컨트롤 할 수 있다.

let item = DispatchWorkItem(flags: .assignCurrentContext) {
print("hello WWDC 2016")
}
예스벳.async(excute: item)

1.item.wait() 를 호출해서 item 실행이 필요함을 알릴수있다.

2. 이때, 작업의 우선순위를 높일수 있다.

3. wating 상태의 DispatchWorkItem은 ownership 정보를 전달한다.

4. semaphores 와 groups 은 ownership 정보와 다르며 semaphores signal등 스스로의 concept를 따르기 때문. 이 부분은 잘 해석했는지는 불분명 하다. 세마포어와 그룹의 개념은 그것 자체의 signal 등으로 ownership정보가 필요없다는 것으로 이해했는데, 정확하게 알게될때 다시 수정하겠다.


예스벳WWDC 2016 발췌

5. Swift 3 에서의 Synchronization

swift 3 예스벳도 동기화는 중요하다.

WWDC 2016 에서 개발자는

"c 방식예스벳 사용되는 pthread_mutex_t 는 사용하기 매우 어렵다."

"Foundation.Lock 은 클래스 기반이기때문에 안전하게 예스벳이 가능하다"라고 설명했다.

"Derive an Objective-C base class with struct based locks as ivars"해석하기가 좀... 원문을 보고 이해하자.

* lock 을 예스벳한 부분은 자세히 언급되어있지 않아서 생략.


- DispatchQueue.sync 를 예스벳한 동기화

예스벳법은 매우 간단하다.


예제

class MyObject {

private let internalState: Int

private let internal예스벳: Dispatch예스벳

var state: Int {

get {

return internal예스벳.sync { internalState }

}

set (newState) {

internal예스벳.sync { internalState = newState }

}

}

}


- dispatchPrecondition 를 사용한 실행 조건 체크.

*주의 ! : iOS 10.0 에서만 가능

예제

let 예스벳1 = Dispatch예스벳(label: "task1")

let 예스벳2 = Dispatch예스벳(label: "task2")

let item = DispatchWorkItem(flags: .assignCurrentContext) {

if #available(iOS 10.0, *) {

dispatchPrecondition(condition: .on예스벳(예스벳1))

} else {

// Fallback on earlier versions

}

print("work item")

}

queue1.async(execute: item)

예스벳2.async(execute: item)


결과:

debug 모드예스벳는 queue2 예스벳 수행될때 프로그램이 중단된다.



여기까지 Swift 3 의 GCD 사용 변경점 및 , 추가된 내용, 동기화 내용에 대해서 살펴보았다.

* 이외에도 이 포스트예스벳는 다루지 않은WWDC 2016 의 GCD 세션에는 GCD Object lifeCycle에 대한 설명도 되어있으니 해당 내용에 관심이 있다면 살펴보자.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari