- GCD : Grand Central Dispatch
- iOS 4부터 지원하는 스레드 관리 기술
- 블록 코딩 기반으로 NSThread, NSOperation보다 쉽게 사용 가능 - GCD Queue
- 실행할 작업을 저장하는 큐로 작업을 정의하고 큐에 넣으면 나머지는 OS에서 알아서 처리
- 블록의 경량화 된 리스트라고 생각하면 됨
- Dispatch Queue
1) 시리얼 디스패치 큐(Serial Dispatch Queue)
* 한 스레드에서 하나의 작업 씩 순차처리(FIFO)하며 처리 외 작업들은 대기
* 호출 할 때 마다 스레드가 추가되므로 과도하게 호출 할 경우 성능에 문제가 생길 수 있음
2) 컨커런트 디스패치 큐(Concurrent Dispatch Queue)
* 작업들을 순서와 상관없이 별도의 스레드에서 실행
* 동시에 실행되는 작업 수는 시스템 상태에 따라 다름(XNU 커널이 알아서 판단 & 관리)
- 직렬 큐
1) 추가된 작업을 순서대로 실행하는 큐(Serial Dispatch Queue)
- 병렬 큐(Global Queue)
1) 추가된 작업을 병렬로 실행하는 큐(Concurrent Dispatch Queue)
2) 기본적으로 4개의 큐가 존재하여 중요도에 따라 HIGH, DEFAULT, LOW, BACKGROUND로 나눠져 실행
- 메인 큐(Main Queue)
1) 추가된 작업을 메인 스레드에서 실행하는 큐(Serial Dispatch Queue)
2) UI작업은 메인큐에서 처리해야 함 - 큐에 작업 추가
- dispatch_async: 비동기로 실행되는 작업 추가
- dispatch_sync: 순차적으로 실행되는 작업 추가
- 실행순서 관련 코드 및 실행 결과
// 1.serial dispatch queue + sync dispatch_queue_t serialQueue1 = dispatch_queue_create("serialQueue", NULL); dispatch_sync(serialQueue1, ^{NSLog(@"working task 1");}); dispatch_sync(serialQueue1, ^{NSLog(@"working task 2");}); dispatch_sync(serialQueue1, ^{NSLog(@"working task 3");}); dispatch_sync(serialQueue1, ^{NSLog(@"working task 4");}); dispatch_sync(serialQueue1, ^{NSLog(@"working task 5");}); // 2.serial dispatch queue + async dispatch_queue_t serialQueue2 = dispatch_queue_create("serialQueue", NULL); dispatch_async(serialQueue2, ^{NSLog(@"working task 1");}); dispatch_async(serialQueue2, ^{NSLog(@"working task 2");}); dispatch_async(serialQueue2, ^{NSLog(@"working task 3");}); dispatch_async(serialQueue2, ^{NSLog(@"working task 4");}); dispatch_async(serialQueue2, ^{NSLog(@"working task 5");}); // 3.concurrent dispatch queue + sync dispatch_queue_t concurrentQueue1 = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(concurrentQueue1, ^{NSLog(@"working task 1");}); dispatch_sync(concurrentQueue1, ^{NSLog(@"working task 2");}); dispatch_sync(concurrentQueue1, ^{NSLog(@"working task 3");}); dispatch_sync(concurrentQueue1, ^{NSLog(@"working task 4");}); dispatch_sync(concurrentQueue1, ^{NSLog(@"working task 5");}); // 1번, 2번, 3번 출력 결과: 순차 실행 //working task 1 //working task 2 //working task 3 //working task 4 //working task 5 // 4. concurrent dispatch queue + async dispatch_queue_t concurrentQueue2 = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(concurrentQueue2, ^{NSLog(@"working task 1");}); dispatch_async(concurrentQueue2, ^{NSLog(@"working task 2");}); dispatch_async(concurrentQueue2, ^{NSLog(@"working task 3");}); dispatch_async(concurrentQueue2, ^{NSLog(@"working task 4");}); dispatch_async(concurrentQueue2, ^{NSLog(@"working task 5");}); // 4번 출력 결과: 비동기 실행 //working task 3 //working task 1 //working task 2 //working task 4 //working task 5
- retainCount 관련 코드 및 실행 결과
// 1. serial dispatch queue + sync dispatch_queue_t t_queue1 = dispatch_queue_create("test.queue1", NULL); NSLog(@"before Working : %lu", (unsigned long)t_queue1.retainCount); dispatch_sync(t_queue1, ^{NSLog(@"working task 1 : %lu", (unsigned long)t_queue1.retainCount);}); dispatch_sync(t_queue1, ^{NSLog(@"working task 2 : %lu", (unsigned long)t_queue1.retainCount);}); dispatch_sync(t_queue1, ^{NSLog(@"working task 3 : %lu", (unsigned long)t_queue1.retainCount);}); dispatch_sync(t_queue1, ^{NSLog(@"working task 4 : %lu", (unsigned long)t_queue1.retainCount);}); dispatch_sync(t_queue1, ^{NSLog(@"working task 5 : %lu", (unsigned long)t_queue1.retainCount);}); NSLog(@"after all Working : %lu \n\n", (unsigned long)t_queue1.retainCount); dispatch_release(t_queue1); // 2. concurrent dispatch queue + sync dispatch_queue_t t_queue2 = dispatch_queue_create("test.queue2", DISPATCH_QUEUE_CONCURRENT); NSLog(@"before Working : %lu", (unsigned long)t_queue2.retainCount); dispatch_sync(t_queue2, ^{NSLog(@"working task 1 : %lu", (unsigned long)t_queue2.retainCount);}); dispatch_sync(t_queue2, ^{NSLog(@"working task 2 : %lu", (unsigned long)t_queue2.retainCount);}); dispatch_sync(t_queue2, ^{NSLog(@"working task 3 : %lu", (unsigned long)t_queue2.retainCount);}); dispatch_sync(t_queue2, ^{NSLog(@"working task 4 : %lu", (unsigned long)t_queue2.retainCount);}); dispatch_sync(t_queue2, ^{NSLog(@"working task 5 : %lu", (unsigned long)t_queue2.retainCount);}); NSLog(@"after all Working : %lu \n\n", (unsigned long)t_queue2.retainCount); dispatch_release(t_queue2); /* 1번, 2번 출력 결과: retainCount가 증가하지 않음 before Working : 1 working task 1 : 1 working task 2 : 1 working task 3 : 1 working task 4 : 1 working task 5 : 1 after all Working : 1 */ // 3. serial dispatch queue + async dispatch_queue_t t_queue3 = dispatch_queue_create("test.queue3", NULL); NSLog(@"before Working : %lu", (unsigned long)t_queue3.retainCount); dispatch_async(t_queue3, ^{NSLog(@"working task 1 : %lu", (unsigned long)t_queue3.retainCount);}); dispatch_async(t_queue3, ^{NSLog(@"working task 2 : %lu", (unsigned long)t_queue3.retainCount);}); dispatch_async(t_queue3, ^{NSLog(@"working task 3 : %lu", (unsigned long)t_queue3.retainCount);}); dispatch_async(t_queue3, ^{NSLog(@"working task 4 : %lu", (unsigned long)t_queue3.retainCount);}); dispatch_async(t_queue3, ^{NSLog(@"working task 5 : %lu", (unsigned long)t_queue3.retainCount);}); dispatch_release(t_queue3); NSLog(@"after Working : %lu \n\n", (unsigned long)t_queue3.retainCount); /* 3번 출력 결과: 작업 수에 따라 retainCount가 증가하지만 블록이 끝나면 감수 before Working : 1 after all Working : 5 working task 1 : 5 working task 2 : 4 working task 3 : 3 working task 4 : 2 working task 5 : 1 */ // 4. concurrent dispatch queue + async dispatch_queue_t t_queue4 = dispatch_queue_create("test.queue4", DISPATCH_QUEUE_CONCURRENT); NSLog(@"before Working : %lu", (unsigned long)t_queue2.retainCount); dispatch_async(t_queue4, ^{NSLog(@"working task 1 : %lu", (unsigned long)t_queue4.retainCount);}); dispatch_async(t_queue4, ^{NSLog(@"working task 2 : %lu", (unsigned long)t_queue4.retainCount);}); dispatch_async(t_queue4, ^{NSLog(@"working task 3 : %lu", (unsigned long)t_queue4.retainCount);}); dispatch_async(t_queue4, ^{NSLog(@"working task 4 : %lu", (unsigned long)t_queue4.retainCount);}); dispatch_async(t_queue4, ^{NSLog(@"working task 5 : %lu", (unsigned long)t_queue4.retainCount);}); dispatch_release(t_queue4); NSLog(@"after Working : %lu \n\n", (unsigned long)t_queue4.retainCount); /* 4번 출력 결과: 작업 수에 따라 retainCount가 증가하지만 블록이 끝나면 감수 before Working : 1 after all Working : 5 working task 1 : 5 working task 2 : 5 working task 3 : 5 working task 4 : 5 working task 5 : 5 */
dispatch_group
- 작업을 그룹단위로 묶어서 관리dispatch_barrier
- 동시에 실행되서는 안되는 작업을 sync/async하게 추가(이 작업 외에 다른 작업들은 대기)
- race condition(하나의 자원 동시 사용) 방지
- 작업이 너무 클 경우 기다리는 시간이 길어져 비효율dispatch_semaphore
- 한 작업 단위보다 작은 부분에서 대기가 필요한 경우(ex: 이미지 로딩 완료 후 UI에 적용)
- wait와 signal의 호출 횟수는 같아야 함(문제가 생길 수 있음)
- MRC에서는 별도 메모리 관리 필요// value값을 count로 가지는 semaphore 생성. dispatch_semaphore_t semaphore = dispatch_semaphore_create(long value); // 호출 시 count값 -1(count가 0 미만이 되는 경우 signal 호출까지 기다림) // 데드락 방지를 위해 시간 설정 가능. // return 값 (작업 완료 후 리턴: 0, 타임아웃: 그 외의 값) dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 계속 기다림 dispatch_semaphore_wait(semaphore, dispatch_time_t timeout); // 시간 지나면 타임아웃 // 호출 시 count +1 dispatch_semaphore_signal(semaphore); //예제 dispatch_queue_t queue = dispatch_queue_create("com.testqueue", NULL); dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_async(queue, ^{ sleep(100); // 바로 실행되지 않게 하기위해 sleep을 걸어 줌 NSLog(@"text1"); dispatch_semaphore_signal(semaphore); }); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"Done1"); /* 결과 text1 Done1 */ // wait 호출과 동시에 count값이 -1이 되고 signal을 기다림. // text1을 출력하고 signal을 만나 count값이 0이되고 Done을 출력.
// 시리얼 디스패치 큐 생성 dispatch_queue_t serialQueue1 = dispatch_queue_create("serialQueue", NULL); dispatch_queue_t serialQueue2 = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL); // 컨커런트 디스패치 큐 생성 dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT); // 글로벌 큐 생성 dispatch_queue_t globalQueue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); dispatch_queue_t globalQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_queue_t globalQueue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); dispatch_queue_t globalQueue4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); // 메인 큐 생성 dispatch_queue_t mainQueue = dispatch_get_main_queue();
// globla queue 생성 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, ^{ /* 이 곳에 작업을 추가해 주세용 */ }); // 그룹에 추가한 작업이 완료될 때 까지 대기. 작업이 완료 시 실행할 작업 큐에 추가 // 해당 메소드 호출 이후에 비동기로 추가된 작업까지 포함하여 기다림 dispatch_group_notify(group, queue, ^{ /* 이 곳에 작업을 추가해 주세용 */ }); // 지정한 시간만큼 또는 그룹에 작업이 완료될 때 까지 기다림 // 스레드를 정지시키므로 백그라운드에서 실행해야 함 // return 값 (작업 완료 후 리턴: 0, 작업이 남아있는 경우: 그 외의 값) dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // 그룹에 작업이 완료되면 리턴 0 dispatch_group_wait(group, dispatch_time_t timeout); // 정해진 시간이 지나면 리턴 !0 // 그룹 안에 새로운 작업의 시작과 끝을 알림. dispatch_group_enter(group); // 작업의 시작. /* 큐에서 처리하는 작업 외의 작업을 입력. 여기 들어가는 작업은 하나의 작업으로 인식됨. */ dispatch_group_leave(group); // 작업의 끝.
// 동시에 실행하면 안되는 작업 추가 dispatch_barrier_async(queue, ^{ /* 이 곳에 작업을 추가해 주세용 */ }); // 비동기 dispatch_barrier_sync(queue, ^{ /* 이 곳에 작업을 추가해 주세용 */ }); // 동기 // 예제 dispatch_queue_t queue = dispatch_queue_create("com.testqueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"Call 1"); }); dispatch_async(queue, ^{ NSLog(@"Call 2"); }); dispatch_async(queue, ^{ NSLog(@"Call 3"); }); dispatch_barrier_sync(queue, ^{ NSLog(@"Call 4-1");}); dispatch_barrier_sync(queue, ^{ NSLog(@"Call 4-2");}); dispatch_barrier_sync(queue, ^{ NSLog(@"Call 4-4");}); dispatch_barrier_sync(queue, ^{ NSLog(@"Call 4-3");}); dispatch_barrier_sync(queue, ^{ NSLog(@"Call 4-5");}); dispatch_async(queue, ^{ NSLog(@"Call 8"); }); dispatch_async(queue, ^{ NSLog(@"Call 9"); }); dispatch_async(queue, ^{ NSLog(@"Call 10"); }); /* 결과 Call 1 Call 3 Call 2 Call 4-1 Call 4-2 Call 4-3 Call 4-4 Call 4-5 Call 8 Call 9 Call 10 */ // barrier로 추가된 작업이 진행되는 동안 다른 작업은 대기. // async로 추가될 경우 (4-1)~(4-5) 작업 순서가 보장되지 않는 것만 다름
'프로그래밍 > iOS' 카테고리의 다른 글
iOS 키보드 숨기는 방법 (0) | 2017.06.28 |
---|---|
KVC(Key-Value Coding) & KVO(Key-Value Observing) (0) | 2017.04.18 |
비동기 처리방법 및 장/단점(GCD 제외) (0) | 2017.04.17 |
setNeedsLayout VS layoutIfNeeded / setNeedsDisplay VS layoutIfDisplay (1) | 2017.04.14 |
Run Loop (0) | 2017.04.12 |