본문 바로가기
프로그래밍 놀이터/iOS

[iOS Study] 병렬 프로그래밍 가이드 ( dispatch queue )

by 돼지왕 왕돼지 2018. 2. 1.
반응형

 [iOS Study] 병렬 프로그래밍 가이드 ( dispatch queue )


https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1

@autorelease block, attribute, Block, block object set, block task, cache, calling scope, CFRunLoopRef, Completion, concurrent, concurrent dispatch queue, concurrent queue, context data, context pointer, COPY, custom context, custom context info, data, dead lock, dealloc, dispatch group, dispatch queue, dispatch semaphore, dispatch source, dispatch_apply, dispatch_apply_f, dispatch_async, dispatch_async_f, dispatch_get_context, dispatch_get_current_queue, dispatch_get_global_queue, dispatch_group_async, dispatch_group_create, dispatch_group_t, dispatch_group_wait, dispatch_main, DISPATCH_QUEUE_CONCURRENT, DISPATCH_QUEUE_PRIORITY_BACKGROUND, DISPATCH_QUEUE_PRIORITY_DEFAULT, DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_PRIORITY_LOW, dispatch_queue_t, dispatch_resume, dispatch_semaphore_create, dispatch_semaphore_signal, dispatch_semaphore_wait, dispatch_set_context, dispatch_set_finalizer_f, dispatch_suspend, dispatch_sync, dispatch_sync_f, dispatch_queue_create, FIFO, future extension, global concurrent queue, global dispatch queue, global queue, global serial queue, independent, ispatch_queue_t, Kernel, lock inside block, main dispatch queue, notification, NSRunLoop, priority level, private dispatch queue, Queue, queue name, Queue 에 task 추가하기, queue-related technologies, reference counted object, release, retain, run loop, scalar var, semaphore, serial, serial queue, Synchronous, system event, task, task group, thread, thread safe, thread 의존, Var, [iOS Study] 병렬 프로그래밍 가이드 ( dispatch queue ), __block, 동기, 병렬 프로그래밍 가이드, 비동기


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 를 사용하자.




반응형

댓글