iOS: Grand Central Dispatch 기초, 질문과 답변

발생일: 2016.05.23

키워드: Grand Central Dispatch, GCD, 디스패치 큐, Dispatch Queue, 

문제:
GCD를 처음 알게 되었을 때 정리해둔 기초 수준의 질문과 답변이다.

해결책:

#
튜토리얼
GCD 기본

GCD 상세

간결하게 잘 설명해둔 포스트 (한글)


Q.
디스패치 큐는 그럼 스레드의 그룹인가? 어떤 종류가 있는 건가?

A.
디스패치 큐에는 시리얼한 것과 컨커런트한 것이 있다.

디스패치 큐
- 시리얼 디스패치 큐
- 컨커런트 디스패치 큐

시스템에서 미리 만들어진 큐는,
메인 디스패치 큐와 글로벌 디스패치 큐가 있다.
- 메인 디스패치 큐 = 시리얼 디스패치 큐
- 글로벌 디스패치큐 = 컨커런트 디스패치 큐


Q.
큐 안에서는 시작 순서가 보장되지만, 끝나는 시간은 알 수 없다고 한다.
각 큐 안에서 여러 개의 스레드가 동작하는 거 맞나?

A.
그렇다. 컨커런트 디스패치 큐가 이렇게 동작한다.




Q.
그럼, 특별히 dispatch_async 를 정의하지 않으면, 개발자가 작성하는 나머지 코드는 메인 스레드에서 동작한다고 보면 될까?

A.
그렇다고 보면 된다. 질문에선 메인 '스레드'라고 표현했는데, 그렇기도 하고 메인 '큐'이기도 하다.


Q.
UI는 메인 스레드에서만 동작하는 것 맞는지?

A.
맞다.


Q.
그럼 별도로 메인큐를 지정해서 쓰는 건 어떤 때인가?

A.
예를 들어, 백그라운드 작업 이후에 메인 UI를 갱신하고 싶다거나, Notification 을 전달하고 싶다거나 할 때 사용할 수 있다.


Q.
dispatch_get_global_queue() 로 큐를 가져올 때, identifier 로 quality of service 를 지정하도록 되어 있더라. (background, low, default, and high)
4가지 종류가 있던데, 그럼 시스템에서 일단 우선순위에 따라 4가지 큐를 준비해뒀다는 건가.

A.
그렇다.


Q.
시스템에서 정한 큐가 아닌 다른 큐를 사용할 때, 우선순위에 관계 없이 설정해도 되는 건가?

A.
우선순위는 필수로 지정하지는 않아도 된다. 필요하다면, dispatch_set_target_queue 로 우선순위를 설정할 수 있기도 하다.


Q.
각 큐에서는 인스턴스의 멤버 변수에 다 접근할 수 있는 건가?

A.
그렇다.


Q.
그러면, 스트럭처 중에 스레드 안전한 스트럭처들은 어떤 건가?

A.
스레드 안전한 스트럭처들


Q.
dispatch_once는 같은 큐 내에서만 유일한 건가? 아님 전체 큐에 대해서 유일한가?

A.
전체 디스패치 큐에 대해 유일하다.
아래 코드로 테스트해볼 수 있다.

- (void)testMakeOnce {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        [self makeOnce];
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        [self makeOnce];
    });
    dispatch_async(dispatch_get_main_queue(), ^{
        [self makeOnce];
    });
    dispatch_async(dispatch_get_main_queue(), ^{
        [self makeOnce];
    });
}

- (void)makeOnce {
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        NSLog(@"%@", foo);
    });
}


Q.
dispatch_create_queue() 로 큐를 생성할 때, 큐 이름(dispatch_queue_attr_t)이 동일하다면 매번 같은 큐를 가져오는 것 맞나?

A.
아니다. 인자로 전달하는 문자열 값은 단순히 디버깅하는 용도이다.
같은 이름으로 큐를 공유할 목적이라면, 큐를 한 번만 생성해서 재사용해야 한다.

+ (dispatch_queue_t)sharedQueue
{
    static dispatch_queue_t sharedQueue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedQueue = dispatch_queue_create("MyQueue", NULL);
    });
    return sharedQueue;
}


Q.
여러 개의 비동기 작업을 대기할 수 있나? (예: 여러 작업을 대기 후 완료 처리할 목적)

A.
dispatch_group 을 사용하면 된다.

dispatch_group_create() 로 그룹을 생성할 수 있고, 이 그룹은 여러 개의 다른 큐와도 동작한다.
그룹 내 작업을 동기로 (블럭킹한 채로) 대기할 수도 있고, 비동기로 해당 작업이 완료된 후에 다른 작업을 수행할 수도 있다.

- 동기로 (블럭킹한 채로) 대기:
    dispatch_group_wait() 은 그룹 내 작업이 완료될 때까지 기다린다.
    대기하는 동안은 그룹핑을 수행하는 스레드를 블럭시키며, 타임아웃을 설정할 수 있다.

- 비동기로 완료 후 수행:
    dispatch_group_notify() 를 사용하면 스레드를 블럭시키지 않고 처리할 수 있으며, 완료 시 코드가 실행될 큐도 설정할 수 있다.
    타임아웃은 설정할 수 없다.


Q.
현재 스레드에서 다른 큐(또는 스레드)에서의 비동기 작업을 (블럭킹한 채로) 기다릴 수 있나?
(예: 비동기 작업을 순차적으로 진행한 후, 다음 작업을 진행)

A.
dispatch_semaphore 를 사용하면 된다.

dispatch_semaphore_create(count); // count = 세마포어 카운트
dispatch_semaphore_wait(); // 세마포어 카운트를 감소시킨다. (카운트가 0보다 작으면 스레드를 중지하고, 대기한다)
dispatch_semaphore_signal(); // 세마포어 카운트를 증가시킨다. (카운트가 0 이상이 되면 스레드를 깨운다) 


Q.
큐를 잘못 사용하고 있는 예

- (void) handleDoSomethingButton{
     [mySpinner startAnimating];

     dispatch_async (dispatch_get_main_queue(), ^{
          (do something lengthy)
          [mySpinner stopAnimating];
    });
}


긴 작업은 백그라운드에서 하는 게 맞는데, 위 샘플은 긴 작업을 그냥 run loop으로 미뤄둔 것과 동일하다 (자바스크립트처럼)


저작자 표시 비영리 변경 금지
신고

카테고리

분류 전체보기 (682)
About me. (6)
Daylogs (647)
영어공부 (0)
비공개 (0)
My works - 추억 (29)