- sync 는 current thread 를 작업이 다 끝날 때까지 block 한다
- async 는 current thread 를 건드리지 않는다. 새로운 thread를 따로 만들거나 해서 실행시킨다.
그러니 어느 쪽이 먼저 실행될 지는 알 수 없다.
- GCD 이전엔 두 가지 방법이 있었다.
- 내장된 동기화 블록 사용하기 -> 남용하면 위험하다. 모든 블록이 서로를 동기화할 것이다.
- NSLock 객체를 직접 사용하기
- 하지만 둘 다 문제가 있다. 데드락에 대한 처리가 힘들기 때문.
- 대안은 GCD 를 사용하는 것이다.
예를 들어 property 의 경우 읽기, 쓰기 등의 operation 을 하나의 serial queue 에서 하도록 만들 수 있다.
_syncQueue = dispatch_queue_create("com.effectiveobjectivec.syncQueue", NULL);
- (NSString*)someString {
__block NSString *localSomeString;
dispatch_sync(_syncQueue, ^{
localSomeString = _someString;
});
return localSomeString;
}
- (void)setSomeString:(NSString*)someString {
dispatch_sync(_syncQueue, ^{
_someString = someString;
});
}
-
사실 setter 는 굳이 sync 여야 할 필요가 없다. 뭔가 return 하는 게 아니니까. 그러니 async 로 바꾸자.
(getter 를 async 로 바꾸면? localSomeString가 nil인 채로 return 될 것이다.) -
비동기의 경우 실행할 블록을 복사해야 하기 때문에
(블록을 복사하는 데 걸리는 시간 > 블록을 실행할 때 걸리는 시간)일 때는 sync 가 더 빠르다
하지만 실행 시간이 더 긴 경우에는 async 가 더 좋은 방법이다. -
serial queue 로 되어있는 걸 concurrent queue 로 바꾸고 대신 barrier 를 도입하자.
barrier 는 queue 의 모든 다른 블록과는 배타적으로 실행된다.
(현재 실행 중인 모든 블록이 끝나길 기다림 -> barrier 블록 실행 -> barrier 블록 끝나면 다시 작업 재개)
물론 concurrent queue 에서만 의미가 있는 기능이다.
_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- (NSString*)someString {
__block NSString *localSomeString;
dispatch_sync(_syncQueue, ^{
localSomeString = _someString;
});
return localSomeString;
}
- (void)setSomeString:(NSString*)someString {
dispatch_barrier_async(_syncQueue, ^{
_someString = someString;
});
}