본문 바로가기
개발 블로그/아이폰개발

[iOS GCD(Grand Central Dispatch)] 기본 2 단계

by snapshot 2020. 1. 9.

이제 실제  GCD를 사용하는 방법 및 주의사항을 설명해보도록 할게요. 

 

let sirialQueue = DispatchQueue(label: "free")
// sirialQueue create

let sirialQueue01 = DispatchQueue(label: "free")
// sirialQueue01 create

위에 시리얼 디스패치 큐 같은 경우는 시리얼이지만 각각 하나의 작업을 실행시 별도의 쓰레드에서 동작을 한다. 

 

시스템은 각각의 시리얼 디스패치 큐를 위해 스레드를 생성한다. 만약 1000개의 시리얼 디스패치 큐를 생성한다면, 1000개의 스레드가 생성된다. 많은 스레드는 많은 메모리를 시스템이 느려지게 하는 원인이 된다. 

 

 

기본 1 단계에서 시리얼 디스패치큐를 생성하고 블록 안에 실행할 작업을 구현하는 것과 시리얼 디스패치 큐를 여러개 생성하는 것은 전혀 다른겁니다. 

 

 

기본 1단계에서 멀티스레드 프로그래밍의 문제점 중 하나인 동일한 변수에 멀티스레드들이 데이터를 업데이트할 때 발생하는 데이터 불일치(레이스 컨디션) 문제를 피하기 위해서는 시리얼 디스패치 큐를 사용해야 한다. (밑에서 컨커런트 디스패치 큐에서는 dispatch barrier async 가 있으니 그건 밑에 설명)

 

 

 물론 상황에 따라 다르겠지만 기본 1단계에서 말했듯이 일관성 없는 데이터로 문제를 발생하지 않고 동시에 실행하기 원한다면 컨커런트 디스패치 큐를 사용하는게 낫다. 왜냐하면 커널에서 효과적으로 알아서 스레드를 잘 관리를 해주기 때문이다. 

 

그런데..objectvie c 에서는 dispatch의 release가 있었는데.. swift에서는 왜 없지..?? 

 

 

메인 디스패치 큐/ 글로벌 디스패치 큐

위에건 기본 내용이고 우리가 사용하는건 대부분 시스템에서 이미 제공하는 메인 디스패치큐 와 글로벌 디스패치 큐를 사용 할 것이다. 

 

메인 디스패치 큐는 메인 스레드에서 작업을 실행하는 큐이다. 참고로 메인 스레드는 하나만 존재하듯이 시리얼 디스패치 큐이다. 하나의 프로그램이 실행할려면 메인스레드는 무조건 필요하다. 유저  인터페이스 업데이트처럼 메인 스레드에서 처리해야 하는 작업등른 메인 디스패치 큐를 사용해야 한다. 

 

글로벌 디스패치 큐는 애플리케이션 어디서나 사용할 수 있는 컨커런트 디스패치 큐이다. 특별한 이유가 없다면 우리는 대부분 글로벌 디스패치 큐를 얻고 사용할 것이다. 

여러 중요도가 있는데..사실 백그라운드 빼고는 잘 모르겠다..어디서 어떻게 제대로 사용해야하는지..이건 공부하고 더 업데이트를 해야겠다.

 

 

dispatch_after

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 3초 후에 로그아웃
});

dispatch_after는 큐의 작업의 시작 타이밍을 설정할 때 사용 한다. 지정된 시간 이후에 작업을 실행할려고 할 때 사용!

위에 코느는 3초 후에 로그아웃을 하라고 것인데..

이게 3초가 우리가 스톱워치로 3초를 딱 맞춰서 되지는 않는다. 대략적으로 3초후와 3초 + 1/60초 후 사이에 실행된다. 그러니 정확한 타이머를 원한다면 문제가 되지만, 대략적으로 지연 작업을 원할 경우 사용하면 매우 좋음!!

 

 

디스패치 그룹

디스패치 그룹은 큐의 그룹을 만들기 위해 사용된다. 이건 컨커런트 또는 멀티 디스패치 큐에서 모든 작업이 종료되고 어떤 작업을 완료하기 위해서 사용하게 된다. 

let concurrentQueue = DispatchQueue(label: "concurrentFree", attributes: .concurrent)

let dispatchGroup = DispatchGroup()
concurrentQueue.async(group: dispatchGroup) {
    print("group01")
}

concurrentQueue.async(group: dispatchGroup) {
    print("group01")
}

concurrentQueue.async(group: dispatchGroup) {
    print("group02")
}

concurrentQueue.async(group: dispatchGroup) {
    print("group03")
}

dispatchGroup.notify(queue: concurrentQueue) {
    print("그룹 끝끝")
}

디스패치 그룹은 디스패치큐의 타입에 관계없이 완료할 작업을 모니터할 수 있다. 

 

마지막은..

dispatch_barrier_async 인데..

이건 나중에 써야겠다..

간단히 말하자면 아까 데이터베이스 또는 파일 엑세스를 할때 레이스 컨디션 방지를 위해서 시리얼 디스패치 큐를 써야 한다고 했지만...컨커런트 디스패치 큐로 읽기를 하고 쓰기는 시리얼로 작업을 하는 것 보다는 dispatch_barrier_async를 사용한다면 같은 시간에 오직 하나의 작업만 실행이 될 수 있다.

 

이런식의 그림이 가능하다.

 

 

 

참고 : "iOX와 OS X의 메모리 관리와 멀티스레딩 기법"

댓글