회사에서 동기화 관련 작업이 교착상태에 걸려서 수정한 적이 있습니다. 그때의 기억을 떠올려 한번 정리해두려고 합니다. 동기화를 직접 제어하는 일은 생각보다 어려운 일이고 요즘은 대부분 비동기로 처리를 합니다. 하지만 동기가 필요한 경우도 있는데요. 오늘은 Objective C 동기화 관리에 대해서 말해보겠습니다.
[목차]
- Objective C 문법 - synchronized
- GCD란?
- GCD는 언제 어떻게 사용되는지
1. Objective C 문법 - synchronized
가끔 IOS 앱을 보면 소스의 일부분이 Objective C로 짜여져있는 부분이 있습니다. 예전부터 이어져온 레거시 코드가 있는 것인데 UI 껍데기는 Swift 언어이고 비즈니스 로직은 Objective C인 경우가 많았습니다. 특히 비즈니스 로직에는 비동기, 동기로 처리되는 함수와 기능이 많았는데 sychronized 블록이 교착상태에 걸려서 제대로 처리되지 않은 경우가 있습니다.
sychronized 문법은 여러 스레드에서 공유된 리소스를 동시 접근을 방지할 수 있게 하는 Objective C의 문법입니다. 여러 스레드에서 하나의 리소스에 접근할 때 경쟁 상태가 됨으로 마비될 수 있습니다. 그러므로 synchronized 블록에 담기면 하나의 스레드가 독점적인 접근을 허용하고 나머지 스레드는 접근하지 못하게 되어 안전성이 보장됩니다.
@synchronized(self) {
// 공유 리소스에 대한 작업 수행
}
- (void)removeItem:(id)item {
@synchronized(self.sharedArray) {
[self.sharedArray removeObject:item];
}
}
데이터를 추가하거나 삭제할 때 각 행위에 대한 독립성이 보장되어야 하므로 동기화를 맞춰줘야 합니다. 보통 데이터베이스에서 데이터가 순서에 맞지 않게 입력되거나 잘못 입력되면 트랜잭션 처리하여 롤백하기도 하는데 Objective C로 다루는 FMDB에서는 그런 처리가 제대로 지원되지 않아서인지 일일이 @synchronized를 해줘야 했습니다.
경쟁상태 (Race Condition)은 요즘 프로그램은 미리 작업이 되어 있어서 잘 발생하지 않는 상태긴 하지만 두 개 이상의 스레드가 동시에 공유된 데이터에 접근하고 수정을 시도할 때 발생합니다. 데이터가 손상되거나 제대로 처리되지 않을 수 있습니다.
데드락(Deadlock) 은 여러 스레드가 상호배제 상태에 빠져서 서로를 대기하게 되어 무한 대기 상태에 빠지는 것을 말합니다. 이를 해결하기 위해서는 자원 할당 순서를 변경하거나 타임아웃으로 synchronized를 해제하여야 합니다. 한 마디로 @synchronized 문법을 잘 사용해야 합니다.
2. GCD(Grand Central Dispatch)란?
GCD란 동시성, 비동시성 처리를 위한 관리 프로그래밍 기술입니다. 애플에서 개발했습니다. Swift와 Objective C에서도 사용할 수 있습니다. 특히 다수의 작업을 동시에 처리하려고 할 때 요긴하게 쓰입니다.
제일 많이 쓰는 GCD의 문법은 Dispatch Queue입니다. 이는 FIFO(First In First Out)으로 큐를 이용하여 작업 스레드에 할당해 줍니다. 디스패치큐에는 여러 작업을 동시에 수행할 수 있게 해주는 Concurrent Queue (병렬 큐)가 있고 한 스레드에 하나씩 수행시켜 주는 Seral Queue (직렬 큐)가 있습니다. 병렬 큐는 리스크가 있어서 많이 사용하진 않습니다.
그 밖에 Dispatch Work Item과 Dispatch Group이 있습니다. 디스패치 그룹으로 작업이 끝났을 때 이벤트 처리를 할 수 있습니다. Work Item은 하나의 작업 스레드 단위로서 큐에 추가할 수 있습니다.
GCD를 사용하면 데이터 다운로드, 사진 불러오기, 채팅 패킷 전송 등 다양한 비동기 처리 작업이나 동기처리 작업을 직접 스레드풀을 만들 필요 없이 수행할 수 있습니다. 자동으로 작업 스레드를 관리해 줌으로써 저희는 이용만 하면 됩니다.
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 동시에 스레드를 사용하는 디스패치를 만듭니다.
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
// 이 쓰레드들을 관리하기 위한 dispatch_group을 만듭니다.
dispatch_group_t group = dispatch_group_create();
// 이 쓰레드들을 그룹에 하나씩 추가합니다. 동시에 실행될 스레드입니다.
for (int i = 0; i < 5; i++) {
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"Task %d started", i);
sleep(1); // Simulate a time-consuming task
NSLog(@"Task %d finished", i);
});
}
// 쓰레드가 끝날 때까지 기다립니다.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// 완료 메시지가 생성되면서 끝났음을 알립니다.
NSLog(@"All tasks completed");
// 메모리 해제 합니다. group
dispatch_group_release(group);
// 메모리 해제 합니다. queue
dispatch_release(concurrentQueue);
}
return 0;
}
3. GCD는 언제 사용해야 할까?
GCD를 제일 많이 사용하는 경우는 요즘은 비동기 작업을 수행할 때입니다. dispatch_async입니다. 비동기는 서버로부터 데이터를 가져올 때 주로 사용합니다. 그다음에 dispatch_get_main_queue()를 통하여 UI에 반영합니다.
데이터베이스에 값을 넣을 때도 GCD를 사용합니다. 데이터가 많아지면 시간이 오래 걸리므로 병렬처리를 하기도 하며 백그라운드 스레드에서 먼저 돌리기도 합니다.
안드로이드의 코루틴처럼 일정 간격 동안 타이머 같이 어떤 함수를 실행시켜야 할 때도 사용합니다. 작업을 지연시킬 수도 있고 동기화를 할 때도 사용합니다.
// 수행할 비동기 작업
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 시간이 소요되는 작업이나 백그라운드 작업을 여기서 수행합니다.
// 예를 들어, 네트워크 요청이나 파일 처리를 수행할 수 있습니다.
// 작업이 완료되면 메인 스레드에서 UI 업데이트를 수행합니다.
dispatch_async(dispatch_get_main_queue(), ^{
// 여기서 UI 업데이트를 수행합니다. 이 코드는 메인 스레드에서 실행됩니다.
// 예를 들어, 레이블 텍스트를 업데이트하거나 이미지를 표시할 수 있습니다.
});
});
[함께 읽으면 좋은 글]
2023.03.15 - [프로그래밍_] - Nest.js 서버 프레임워크 기본적인 지식
2023.04.02 - [프로그래밍_] - Intellij 무료, 유료 차이 및 사용법 Springboot
2023.02.27 - [프로그래밍_] - Node.js 데이터 파싱 1) cheerio 설치 및 파싱
'프로그래밍_' 카테고리의 다른 글
XCODE IOS 프로파일링 디버깅 하는 방법 (0) | 2023.04.10 |
---|---|
Intellij 무료, 유료 차이 및 사용법 Springboot (0) | 2023.04.02 |
책의 목차로 메모하고 정리하는 앱, MOKCHA 개발 동기 설명 (0) | 2023.03.28 |
네이버 블로그 SEO 프로젝트 - Next.js Api Route 사용 (0) | 2023.03.26 |
네이버 블로그 SEO 프로젝트 - 개발계획, 기술 (0) | 2023.03.21 |
댓글