44. Dispatch Group 메커니즘을 통해 시스템 리소스 상태에 따라 작업 수행

6058 단어
《고품질 iOS와 OS X 코드를 작성하는 52가지 효과적인 방법》-제6장 제44조(ps: 이것은 독서노트입니다. 기억을 깊이 있게 하고 참고만 제공)

제44조: Dispatch Group 메커니즘을 통해 시스템 자원 상황에 따라 임무를 수행한다.


dispatch group은 GCD의 특성으로 작업을 그룹화할 수 있습니다.호출자는 이 작업이 끝날 때까지 기다릴 수도 있고, 리셋 함수를 제공한 후에 계속 아래로 실행할 수도 있으며, 이 작업이 끝날 때 호출자는 알림을 받을 수 있다.이 기능은 여러 가지 용도가 있는데 그 중에서 가장 중요하고 주의할 만한 용법은 동시에 실행할 여러 개의 임무를 한 조로 묶는 것이다. 그래서 호출자는 이 임무들이 언제 모두 집행될 수 있는지 알 수 있다.
다음 함수는 dispatch group을 만들 수 있습니다.
dispatch_group_t group = dispatch_group_create();

dispatch group은 간단한 데이터 구조입니다. 이런 구조는 서로 다를 것이 없습니다. 이것은 발송 대기열과 같지 않고 후자는 신분을 구별하는 표지부호도 있습니다.임무를 편성하려면 두 가지 방법이 있다.첫 번째는 다음과 같은 함수입니다.
void dispatch_group_async(dispatch_group_t group,
                          dispatch_queue_t queue,
                          dispatch_block_t block);

얘는 그냥 디스패치Async 함수의 변체는 원래보다 한 개 더 많은 함수로 실행될 블록이 귀속된 그룹을 표시하는 데 사용됩니다.임무가 속한 dispatch group을 지정하는 방법도 있습니다. 바로 다음 함수 쌍입니다.
dispatch_group_enter(dispatch_group_t group)
dispatch_group_leave(dispatch_group_t group)

전자는 그룹에서 수행하고자 하는 임무 수를 점차 늘릴 수 있고, 후자는 점차 줄일 수 있다.디스패치가 호출되었음을 알 수 있습니다group_enter 이후 대응하는 디스패치group_leave가 필요해.이는 인용계수(제29조 참조)와 비슷하므로 인용계수를 사용해야 한다.메모리 유출을 막기 위해 보류 작업과 방출 작업이 서로 대응해야 한다.
다음 함수는 dispatch group이 실행될 때까지 기다릴 수 있습니다.
void dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout)

이 함수는 두 개의 인자를 받아들인다. 하나는 기다리는 그룹이고, 다른 하나는 기다리는 시간을 대표하는timeout 값이다.timeout 매개 변수는 dispatch 그룹이 실행되기를 기다리는 동안 함수가 얼마나 막혀야 하는지를 나타냅니다.dispatch group을 실행하는 데 필요한 시간이 timeout보다 적으면 0을 되돌려줍니다. 그렇지 않으면 0이 아닌 값을 되돌려줍니다.이 매개변수는 DISPATCH 상수를 가질 수도 있습니다.TIME_FOREVER, 이 함수는 dispatch group이 끝날 때까지 기다리며 시간을 초과하지 않습니다 (time out).위의 함수를 사용하여 dispatch group이 실행될 때까지 기다릴 수 있을 뿐 아니라 다음 함수를 사용하여 다른 방법을 사용할 수도 있습니다.
void dispatch_group_notify(dispatch_group_t group,
                           dispatch_queue_t queue,
                           dispatch_block_t block);

wait 함수와 약간 다른 것은 개발자가 이 함수에 블록을 전송할 수 있으며 디스패치 그룹이 실행된 후에 블록은 특정한 라인에서 실행됩니다.현재 이 안건에 가입하는 것은 막아서는 안 되고 개발자가 그 임무를 모두 완수할 때 통지를 받고자 한다면 이 방법은 매우 필요하다.
그룹의 모든 대상이 특정한 작업을 수행하고 모든 작업이 끝날 때까지 기다리려면 이 GCD 기능을 사용하면 됩니다.코드는 다음과 같습니다.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_group_t dispatchGroup = dispatch_group_create();

NSArray * collection;
for (id object in collection) {
    dispatch_group_async(dispatchGroup, queue, ^{
        [object description];
    });
}

dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER);
//Continue processing after completing tasks

현재 스레드가 막히지 않으면 wait 대신 notify 함수를 사용할 수 있습니다:
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
    //Continue processing after completing tasks
});

notify 리셋할 때 선택한 대기열은 구체적인 상황에 따라 정해야 합니다.필자는 범례 코드에서 주 루틴 대기열을 사용했는데 이런 것은 흔히 볼 수 있는 문법이다.또한 사용자 정의 직렬 또는 전역 동시 큐를 사용할 수도 있습니다.
이 예에서 모든 임무는 같은 대열에 파견된다.그러나 실제로는 반드시 이렇게 해야만 하는 것은 아니다.또한 특정 작업을 우선 순위가 높은 스레드에 배치하고 모든 작업을 하나의 dispatch group에 포함시켜 실행이 완료되면 알림을 받을 수도 있습니다.
dispatch_queue_t lowPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_queue_t highPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

dispatch_group_t dispatchGroup = dispatch_group_create();

NSArray * lowPriorityObject;
NSArray * highPriorityObject;
for (id object in lowPriorityObject) {
    dispatch_group_async(dispatchGroup, lowPriorityQueue, ^{
        [object description];
    });
}

for (id object in highPriorityObject) {
    dispatch_group_async(dispatchGroup, highPriorityQueue, ^{
        [object description];
    });
}


dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
    //Continue processing after completing tasks
});

위와 같이 작업을 병렬 대기열에 제출하는 것 외에, 작업을 각 직렬 대기열에 제출하고 디스패치 그룹으로 실행 상황을 추적할 수 있습니다.그러나 만약 모든 작업이 같은 직렬 대기열 안에 배열된다면 디스패치 그룹은 쓸모가 크지 않을 것이다.이 때 작업은 하나하나 실행되기 때문에 전체 작업을 제출한 후에 블록을 제출하면 됩니다. 이것은 notify 함수를 통해 디스패치 그룹이 실행될 때까지 기다린 후에 블록을 리셋하는 것과 같은 효과입니다.
dispatch_queue_t queue = dispatch_queue_create("com.effectiveobjectivec.queue", NULL);

NSArray *collection;
for (id object in collection) {
    dispatch_async(queue, ^{
        [object description];
    });
}

dispatch_async(queue, ^{
    //Continue processing after completing tasks
});

필자는 왜 제목에서'시스템 자원 상황에 따라 임무를 집행한다'고 말해야 합니까?병발 대열에 임무를 보낸 그 예를 뒤돌아보면 알 수 있을 것이다.GCD는 대기열의 블록을 실행하기 위해 적절한 시기에 새 스레드를 자동으로 생성하거나 이전 스레드를 재사용합니다.만약 병렬 행렬이 있다면, 그 중 여러 개의 라인이 있을 수 있으며, 이것은 여러 블록이 병렬 실행될 수 있다는 것을 의미한다.병렬 대기열에서 임무를 수행하는 데 사용되는 병렬 스레드의 수량은 각 요소에 달려 있고 GCD는 주로 시스템 자원 상황에 따라 이러한 요소를 판정한다.CPU를 넣으면 여러 개의 핵심이 있고 대기열에 대량의 작업이 실행되기를 기다리면 GCD는 이 대기열에 여러 개의 라인을 설치할 수 있다.디스패치 그룹이 제공하는 이런 간편한 방식을 통해 일련의 주어진 임무를 동시에 수행할 수 있을 뿐만 아니라, 모든 임무가 끝날 때 알림을 받을 수 있다.GCD에는 동시 큐 메커니즘이 있으므로 사용 가능한 시스템 리소스 상황에 따라 작업을 동시에 수행할 수 있습니다.
앞의 범례 코드에서 우리는 어떤collection을 두루 훑어보고 그 요소마다 임무를 수행하는데 이것도 다른 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) {
    //Perform task
});

주의해야 할 것이 하나 있다. 디스패치apply에서 사용하는 대기열은 병렬 대기열일 수 있습니다.병렬 대기열을 사용하면 시스템은 자원 상황에 따라 이 블록을 병행할 수 있습니다. 이것은 디스패치 그룹을 사용하는 범례 코드와 같습니다.
dispatch group을 항상 사용해야 하는 것은 아닙니다.그러나, dispatchapply는 모든 작업이 끝날 때까지 계속 막힙니다.이를 통해 알 수 있듯이 블록을 현재 대기열(또는 시스템에서 현재 대기열보다 높은 직렬 대기열)에 보내면 자물쇠가 사라집니다.백그라운드에서 작업을 수행하려면 dispatch group을 사용해야 합니다.

요점


4
  • 일련의 작업은 디스패치 그룹에 귀속될 수 있습니다.개발자는 이 그룹의 작업이 끝났을 때 알림을 받을 수 있습니다

  • 4
  • 디스패치 그룹을 통해 병렬 발송 대기열에서 여러 가지 작업을 동시에 수행할 수 있습니다.이 경우 GCD는 시스템 리소스 상황에 따라 이러한 동시 작업을 스케줄링합니다.개발자가 만약 스스로 이 기능을 실현한다면 대량의 코드를 작성해야 한다
  • 좋은 웹페이지 즐겨찾기