[iOS Study] 병렬 프로그래밍 가이드 ( dispatch queue )
About Dispatch Queues
-
모든 dispatch queue 는 FIFO 이다.
-
다음의 dispatch queue 가 있다.
serial
concurrent
main dispatch queue
-
serial queue ( private dispatch queue ) 는 한번에 하나의 task 만 수행한다.
수행은 다른 thread 에서 수행하는데, 그 thread 는 매번 ( 각 task 마다 ) 다를 수 있다.
serial queue 는 여러 개 만들 수 있고, 각 queue 는 서로 independent 하게 작업을 수행한다.
-
concurrent queue ( global dispatch queue ) 는 한번에 여러 개의 task 를 동시에 수행한다.
그러나 task 들은 순서대로 가져다 수행시킨다.
iOS 5 이상에서는 DISPATCH_QUEUE_CONCURRENT 를 통해 concurrent dispatch queue 를 직접 만들 수 있다.
이를 포함해 4개의 queue type 이 있어 직접 만들어 사용할 수 있다.
-
main dispatch queue 는 globally available 한 serial queue 이다.
app 의 run loop 에서 작동한다.
-
system 은 한번에 수행 가능한 task 의 수를 결정한다.
그래서 1000개의 task 를 동시 수행시킨다고 무조건 동시에 수행되는 것은 아니다.
Queue-related Technologies
-
dispatch group 은 block object set 의 completion 을 모니터링 하는 방법이다.
-
dispatch semaphore 는 전통적인 세마포어와 비슷하지만 약간 더 개선되었다.
dispatch semaphore 는 kernel 에 block 이 될 필요가 있을때만 수행한다.
-
dispatch source 는 특정 system event 에 대해 notification 을 생성한다.
system event 를 모니터링 하기 위해 dispatch source 를 사용할 수 있다.
특정 event 가 발생하면 dispatch source 가 특정 dispatch queue 에 task 를 전달하여 수행한다.
Block 을 이용하여 task 구현하기
-
block 이 synchronous 로 수행될 때에는 __block 이라는 키워드를 붙인 var 를 사용하기도 한다.
__block 이 붙은 녀석을 통해 calling scope 에서 return 값을 전달받는 형태로 구현 가능하다.
-
block 을 사용할 때는 block 에서 scalar var 만 참조하도록 해야지,
큰 structure 나 pointer-based var 를 참조하는 것은 위험하다.
-
dispatch queue 는 add 되는 녀석을 copy 한다.
그리고 수행이 끝나면 release 를 시킨다.
그래서 block 을 copy 해서 넘길 필요가 없다.
-
block 에서 하는 일이 너무 작으면 block 으로 만들어 queue 에 넣지 말자.
-
block 에서 thread 에 의존한 cache 를 하지 말아야 한다.
block 간 무언가 공유를 해야 한다면, dispatch queue 의 context pointer 를 사용하여 공용 데이터를 저장해야 한다.
-
block 이 objective-c object 를 많이 만든다면, 블럭에 @autorelease block 을 넣어 메모리 관리를 시키는 것이 좋을 수 있다.
Dispatch queue 만들고 관리하기
-
system 은 각 앱에 4개의 concurrent dispatch queue 를 제공한다.
이 queue 들은 application global 하고, priority level 만 다르다는 차이가 있다.
global 하기 때문에 생성하는 것이 아니라 dispatch_get_global_queue 를 통해 query 를 해서 get 해야 한다.
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ); // 0 은 future extension, 현재 0 넣으면 됨
-
const 는 다음의 것들이 있다.
DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND
-
dispatch queue 들은 reference-counted object 라 retain, release 를 호출해주지 않아도 된다.
매번 필요할때마다 dispatch_get_global_queue 함수를 호출해서 가져가면 된다.
Serial dispatch queue 만들기
-
concurrent queue 는 만들어져있는 것을 query 해서 사용하지만,
serial queue 는 만들어서 사용해야 한다.
-
dispatch_queue_create 를 통해서 만들 수 있다.
param 은 queue 이름과 queue 의 attr 이다.
dispatch_queue_t queue = dispatch_queue_create(“myQueue”, NULL);
Runtime 에 Common queue 에 접근하기
-
dispatch_get_current_queue 는 현재 queue 를 return 한다.
block 안에서 수행하면 해당 block 이 속한 queue 를 가져온다.
block 안이 아니라면 기본 concurrent queue 를 가져온다.
-
dispatch_get_main_queue 는 app 의 main thread 에 엮여 있는 serial dispatch queue 를 return 한다.
dispatch_main 을 호출하면 이 main queue 에서 호출된다.
CFRunLoopRef 나 NSRunLoop 를 이용해도 main queue 에서 호출한다.
-
dispatch_get_global_queue 를 통해 shared concurrent queue 를 가져올 수 있다.
Custom Context Info 를 queue 에서 저장하기
-
dispatch_set_context 와 dispatch_get_context 함수를 이용하여 custom context 의 data 를 공유할 수 있다.
queue 의 dealloc 에서 context data 를 dealloc 해줄 수 있다.
-
dispatch_set_finalizer_f 를 통해서 finalize 함수를 붙일 수 있다.
이 곳에서 context info 를 dealloc 해줄 수 있다.
Queue 에 task 추가하기
-
queue 에 task 를 추가하는 방법은 크게 2가지 방법이 있다.
동기, 비동기.
-
비동기는 dispatch_async 나 dispatch_async_f 함수를 통해 수행한다.
-
동기는 dispatch_sync 나 dispatch_sync_f 함수를 통해 수행한다.
동기는 해당 block 이 수행을 마칠 때까지 대기한다.
-
!! 현재 queue 를 통해 수행되고 있는 task 에서 동일 queue 에 dispatch_sync 나 dispatch_sync_f 를 호출하면 안 된다.
그럼 dead lock 이 발생한다.
-
loop 를 concurrent 하게 돌리기 위해서는 dispatch_apply 나 dispatch_apply_f 를 호출하는 것이 유용할 수 있다.
이 녀석이 사용가능한 경우는 iteration 을 도는데, 도는 순서보장이 중요하지 않고, 각 iteration 이 다른 일을 수행할 때 호출하기에 좋다.
dispatch_apply 를 호출할 때 for 문 안에서 return 하는 것이 쉽지 않기 때문에 조심해야 한다.
dispatch_apply(count, queue, ^(size_t i) {
// to something
});
-
queue 에 대해 dispatch_suspend 와 dispatch_resume 함수를 부를 수 있다.
이 둘은 각각 suspend reference count 를 가지고 있어서, 제대로 queue 를 재작동 시키려면 suspend 와 resume 의 pair 를 맞추어야 한다.
Dispatch semaphore 를 사용하여 유한한 자원 관리하기
-
queue 에 들어간 block 이 유한한 자원에 접근한다면,
semaphore 를 사용하여 control 해주어야 하는 상황이 발생할 수 있다.
dispatch semaphore 는 일반 semaphore 와 한가지만 뺴고 같다.
일반 semaphore 보다 빠르다는 것.
kernel 이 특정한 경우에 kernel 단계까지 이 semaphore 를 전달하지 않기 떄문에..
-
dispatch_semaphore_create 함수를 통해서 semaphore 를 생성하고, 이 때 사용 가능한 res 갯수를 int 로 전달한다.
각 task 는 dispatch_semaphore_wait 함수를 통해 semaphore 를 기다리고,
wait 가 끝나면, resource 를 얻어 일을 수행한다.
task 가 끝나면 dispatch_semaphore_signal 함수를 통해 resource 사용이 끝남을 알려준다.
Queue 된 task group 끝나기를 기다리기
-
어떤 block 들을 group 으로 묶어서 그 group 작업이 끝난 후에 특정 block 을 수행시키고 싶다면..
dispatch_async 대신에 dispatch_group_async 를 통해서 작업을 수행시키면 된다.
그리고 dispatch_group_wait 를 통해 그 특정 그룹이 끝나기만을 기다릴 수 있다.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
// some action
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER );
dispatch_release(group);
Dispatch queue 와 thread safety
-
dispatch queue 에 block 을 등록하는 것은 thread safe 하다.
-
dispatch_sync 를 호출할 떄는 호출하는 녀석이 sync 에 전달되는 queue 를 통해 수행되는 것이 아닌지 확인해야 한다.
같은 녀석이면 dead lock 에 걸릴 수 있으니 절대 그러면 안 된다.
-
dispatch queue 에 전달되는 block 에서는 lock 을 잡지 않는 것이 좋다.
lock 을 잡으려면 확실히 해야 하고,
만약 serial 을 만드는 거라면 serial dispatch queue 를 사용하자.
'프로그래밍 놀이터 > iOS' 카테고리의 다른 글
[ios] Gallary ( Photo Album) Permission 체크 (0) | 2018.02.03 |
---|---|
[ios] Camera Permission (0) | 2018.02.02 |
[ios] .c 파일을 import 한 후 build 가 안 된다면 pch 파일을 확인해봐라. (0) | 2018.01.31 |
[ios] 압축, 압축해제 ( archive, unarchive ) (0) | 2018.01.30 |
[ios tutorial] Local Notification (0) | 2018.01.29 |
댓글