[iOS-Swift] 동시성 Concurreuncy

TIL 24일 차 - [Swift] 동시성 프로그래밍 Concurrency

 

동시성 프로그래밍 Concurrency

한 번에 여러 작업을 동시에 수행하는 것을 목표로 하는 프로그래밍 방식

 

- 하나의 프로세스(실행 중인 앱 하나)에는 여러 개의 Thread(그 앱 안에서 실제로 일을 하는 일꾼들)가 존재하며,

   각 Thread에서 병렬 작업을 수행 할 수 있음

- 별도로 지정하지 않으면 메인 스레드에서 작업이 진행됨

 

 

메인 스레드

- UI작업을 할 수 있는 유일한 스레드.. ex) 버튼 클릭 처리, UILabel 텍스트 변경, 화면 전환, 애니메이션

- 별도로 스레드로 분리하지 않는다면 모든 작업은 기본적으로 MainThread가 실행됨

- 메인 스레드에서 모든 작업을 처리할 경우 속도 지연 및 성능 저하 문제가 발생할 수 있음

 

그래서 필요한게 GCD이다!!

GCD란? (Grand Central Dispatch)

- GCD는 iOS에서 제공하는 동시성 프로그래밍을 쉽게 처리하기 위한 도구

- 작업을 Queue에 추가하면 OS가 적절한 Thread에 일을 할당하여 처리

우리가 할 일: 1. 작업을 Queue에 넣기. 2. 메인/백그라운드만 구분  -> 나머지는 OS가 알아서 처리해 줌

- DispatchQueue를 사용해 여러 개의 스레드에 업무 분담 가능

- 디스패치 큐는 주로 2가지 큐를 제공하며 (Main Queue, Global Queue)로 구성

- Queue를 사용하므로 FIFO 방식으로 먼저 들어온 업무를 먼저 Thread에 전달

 

Main Queue

- Main Thread로 작업을 전달하는 Queue

- UI 작업 전용

- 들어온 일을 하나씩 처리한다고 하여 Serial Queue(직렬큐)라 불림

DispatchQueue.main.async {
  // UI 작업
}

 

Global Queue

- 백그라운드 스레드

- 무거운 작업 담당

- Global Queue에 할 일을 추가하면 OS는 여러 개의 Thread에 작업을 나누어 처리함

- 들어온 여러 가지의 작업을 동시에 처리할 수 있어서 Concurrent Queue(병렬큐)라 부름

DispatchQueue.global().async {
	// 네트워크 요청
    // 이미지 다운로드
    // 데이터 파싱
    // UI가 아닌 오래걸리는 작업들
}

 

 

실제 iOS에서 자주 쓰는 패턴

DispatchQueue.global().async {
	// 1. 오래 걸리는 작업 (백그라운드)
    
    DispatchQueue.main.async {
    	// 2. 결과로 UI 업데이트
    }
}

 

 

 

QoS (Quality of Service)

- 작업의 급함 정도를 OS에게 알려줘서 CPU, 배터리, Thread를 똑똑하게 쓰게 하는 것

- 작업의 우선순위를 지정하는 (Qos) Quality of Service를 설정할 수 있음

- 우선순위를 통하여 자원 분배를 효율적이게 함

 

.userInteractive

- 가장 높은 우선순위

- 사용자에 즉각적인 반응을 주어야 할 때 사용

- 스크롤, 애니메이션, 터치 이벤트 등

 

.userInitiated

- 사용자가 요청

- 문서열기, 이미지 로드 등

 

.default

 

.utility

- 긴 시간이 걸릴 수 있는 작업

- 백그라운드에서 데이터 동기화, 파일 다운로드 등

 

.background

- 사용자에게 보이지 않거나 중요하지 않은 작업

- 데이터 백업, 데이터 정리 등

 

.unspecified

- QoS가 지정되지 않은 상태로, 시스템이 우선순위를 정하게 함

- 실무에서 거의 사용되지 않음

 

사용 예시 코드

DispatchQueue.global(qos: .background).async {
	// 사용자에게 보이지 않거나 중요하지 않은 작업
}

 

 

 

async vs sync

각각의 Queue에는 동기적으로 작업을 하거나 비동기적으로 작업을 할당 가능

 

sync(동기)

- 작업이 순차적으로 실행

- 현재 작업이 끝날 때까지 다음 작업이 시작되지 않음

- 작업이 끝날 때까지 대기하므로 대기시간이 발생할 수 있음

- 작업이 순서대로 실행되어 코드의 흐름을 이해하기 편하고 디버깅이 간편하지만, 성능저하 발생 가능

DispatchQueue.main.sync {
	print("Hello")
}

print("world")

// sync의 작업이 끝나기 전까지는 print("world") 호출되지 않음

 

async(비동기)

- 작업을 시작한 후, 작업이 완료될 때까지 기다리지 않고 다음 코드를 바로 실행함

- 여러 작업이 병행되어 빠른 처리가 가능하지만 결과를 기다리는 구조로 인해 코드가 복잡해질 수 있음

DispatchQueue.global().async {
	Thread.sleep(forTimeInterval: 1)
    	print("Hello")
}

print("world")

// async(비동기)이므로 바로 print("world")호출 된후 1초 뒤에 "Hello"가 호출 됨