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

[Effective Objective-C] #44 플랫폼 확장의 이점을 얻으려면 디스패치 그룹을 사용하라

by 돼지왕 왕돼지 2017. 10. 9.
반응형

 [Effective Objective-C] #44 플랫폼 확장의 이점을 얻으려면 디스패치 그룹을 사용하라


출처 : Effective Objective-C

CPU, dispatch_apply, dispatch_async, dispatch_block_t, dispatch_get_global_queue, dispatch_group_create, dispatch_group_leave, dispatch_group_notify, dispatch_group_t, dispatch_group_wait, dispatch_group_async, dispatch_group_enter, dispatch_group_wait, dispatch_queue_create, DISPATCH_QUEUE_PRIORITY_DEFAULT, dispatch_queue_t, DISPATCH_TIME_FOREVER, dispatch_time_t, Enter, GCD, Leave, return 값, size_t, [Effective Objective-C] #44 플랫폼 확장의 이점을 얻으려면 디스패치 그룹을 사용하라, 다수 작업, 디스패치 그룹, 병렬 수행, 병렬 큐, 스레드 중단, 쌍, 참조 세기, 코어, 코어 활용, 플랫폼 확장


-

플랫폼 확장이라 함은 CPU 의 다수의 코어를 자동으로 활용하는 이점



-

디스패치 그룹은 그룹 작업을 쉽게 할 수 있게 하는 GCD 기능이다.

이 기능을 사용해 작업들이 모두 끝나기를 기다리거나 작업들이 모두 끝났을 때 콜백을 통해 알림을 받을 수 있다.

이 기능은 몇 가지 이유 때문에 매우 유용하다.

이점 중 첫 번째이자 가장 흥미로운 것은 다수의 작업이 병렬로 수행되길 원하고 모든 작업이 언제 끝났는지 알고 싶을 때다.


다음 함수로 디스패치 그룹을 생성할 수 있다.

dispatch_group_t dispatch_group_create();



-

그룹은 식별자가 있는 디스패치 큐와는 다르게 그룹 자체를 식별할 수 없는 간단한 데이터 구조체로 되어 있다.

두 가지 방법으로 태스크와 디스패치 그룹을 연관 지을 수 있다.


첫번째 방법은 다음 함수를 사용하는 것이다.

void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);


이 함수는 일반적인 dispatch_async 함수의 변형이다.

dispatch_async 와는 다르게 그룹 파라미터를 추가로 받는다.

이 파라미터는 실행될 블록과 연관된 그룹을 의미한다.



-

작업을 디스패치 그룹에 연관 짓는 두 번째 방법은 다음 함수 쌍(pair)를 이용하는 것이다.

void dispatch_group_enter(dispatch_group_t group);

void dispatch_group_leave(dispatch_group_t group);


전자는 그룹이 현재 실행되고 있다고 생각하는 작업 수를 증가시키는 것이고 후자는 그 반대다.

그렇기 때문에 dispatch_group_enter 의 각 호출은 dispatch_group_leave 와 반드시 쌍으로 사용되어야 한다.

이는 누수를 피하기 위해 리테인과 릴리스를 쌍으로 사용해야 하는 참조 세기와 비슷하다.


디스패치 그룹의 경우 enter 와 leave 가 짝이 맞지 않으면 그룹은 절대 끝나지 않는다.



-

다음 함수는 디스패치 그룹이 끝나기를 기다려야 할 때 사용할 수 있다.

long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);


이 함수는 기다려야 할 그룹과 기다릴 시간(timeout)을 값으로 받는다.

timeout 은 이 함수가 그룹이 끝나기를 기다리는 최대 시간을 나타낸다

그룹이 timeout 전에 끝나면 0을 반환한다.

반대인 경우 0이 아닌 값을 반환한다.

timeout 값으로 DISPATCH_TIME_FOREVER 상수를 사용할 경우 함수는 그룹이 끝나지 않으면 영원히 기다린다.



-

다음 함수는 디스패치 그룹이 끝나기를 기다리는 동안 현재 스레드를 중단하는 것의 대안이다.

(dispatch_group_wait 는 그룹이 끝나기 전까지 현재 스레드가 중단된다.)

vodi dispatch_group_notify(dispatch_group_t gorup, dispatch_queue_t queue, dispatch_block_t block);


wait 함수와는 약간 다르다.

이 함수는 그룹이 끝났을 때 특정 큐에서 실행될 블록을 명시할 수 있다.

이렇게 하는 것은 현재 스레드가 중단되지 않고 모든 작업이 종료되는 것을 알아야 할 때 유용하다.



-

예제 코드들

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_group_t dispatchGroup = dispatch_group_create();

for (id object in collection){

     dispatch_group_async(dispatchGroup, queue, ^{

          [object performTask];

     });

}


dispatch_queue_t notifyQueue = dispatch_get_main_queue();

dispatch_group_notify(dispatchGroup, notifyQueue, ^{

          // 코드

});



-

GCD 는 자동으로 새로운 스레드를 생성하거나 이전 것을 재사용한다.

그것이 큐에 있는 블록들을 처리하기에 적합해 보이기 때문이다.

이는 다수의 블록이 동시에 실행됨을 의미한다.

주어진 병렬 큐에서 처리하는 병렬 스레드 수는 GCD가 결정하는 팩터(factor)에 달려 있고 주로 시스템 리소스에 기반을 둔다.

CPU 코어가 여러 개이면 할 일이 많은 큐는 수행할 스레드를 많이 줄 것이다.

디스패치 그룹은 주어진 작업들을 동시에 수행하게 하는 쉬운 방법을 제공하고 작업 그룹이 끝나면 알림을 준다.

GCD 병렬 큐의 본성 때문에 작업들은 동시에 실행된다.

그리고 이는 사용 가능한 시스템 리소스에 기반을 둔다.

이로 인해 개발자는 비지니스 로직에 집중할 수 있고 병렬 작업을 다루는 복잡한 스케줄을 작성할 필요가 없다.



-

컬렉션을 이용해 반복하고 각 아이템마다 작업을 수행하는 예제는 또 다른 GCD 함수를 이용해서도 할 수 있다.

void dispatch_apply(size_t iterations, dispatch_queue_t queue, void(^block)(size_t));


이 함수는 주어진 수만큼 블록을 반복 수행한다.

각 반복마다 0부터 iterations -1 까지 차례로 증가된 값을 전달한다.

dispatch_queue_t queue = dispatch_queue_create("com.effectiveobjectivec.queue", NULL);

dispatch_apply(10, queue, ^(size_t i){

     // task

});


dispatch_apply 를 사용할 때 반드시 알아야 할 것은 큐가 병렬 큐일 수 있다는 사실이다.

그렇다면 블록은 디스패치 그룹의 예처럼 시스템 리소스에 따라 병렬로 수행될 것이다.


dispach_apply 는 모든 반복이 끝나기 전까지 현재 스레드를 중단한다.

이러한 이유 때문에 현재 큐에서(또는 계층 구조상 현재 큐 위에 있는 순차 큐에서) 블록들을 수행하려 한다면 데드락이 발생할 것이다.

백그라운드로 작업이 수행되길 원한다면 디스패치 그룹을 사용해야 한다.




기억할 점


-

디스패치 그룹은 작업들을 그룹 짓기 위해 사용된다.

선택적으로 그룹이 실행이 끝났을 때 알림을 받을 수 있다.


-

디스패치 그룹은 병렬 디스패치 큐에 있는 다수의 작업을 병렬로 실행하려 할 때 사용될 수 있다.

이 경우 GCD 는 다수의 작업을 시스템 리소스에 기반을 두고 동시에 수행되도록 스케줄링한다.

이를 직접 구현하려면 많은 코드를 작성해야 한다.




반응형

댓글0