티스토리 뷰

iOS

[iOS] - 프로세스와 스레드 관리 방법

강철곰탱이 2025. 6. 24. 00:36

 

📝 목차

1. 프로세스와 스레드의 차이점
2. 멀티 스레딩이 필요한 이유는?
3. GCD(Grand Central Dispatch)

 


1. 프로세스와 스레드의 차이점

 

프로세스: 운영체제가 자원을 할당하여 실행하는 독립적인 프로그램의 인스턴스
스레드: 프로세스 내에서 실행되는 작업 흐름의 단위

 

1️⃣ 프로세스

 

프로세스는 각각 독립된 메모리 영역(Code, Data, Stack, Heap)을 할당받는다.

기본적으로 프로세스당 최소 1개의 스레드(메인 스레드)를 가지고 있다. 각 프로세스는 독립적인 메모리 공간을 가지며, 다른 프로세스와 메모리를 직접적으로 공유하지 않는다. 

 

 

 

2️⃣ 스레드

 

스레드는 프로세스 내에서 각각 Stack만 따로 할당받고 스택을 제외한 Code, Data, Heap 영역은 공유해 서로 읽고 쓸 수 있다. 스레드는 프로세스보다 생성 및 전환이 빠르고, 자원소모가 적다. 하지만 한 스레드의 오류가 프로세스 전체에 영향을 줄 수 있다.

 

 

 

✏️ 멀티 프로세싱

 

: 하나의 응용 프로그램을 여러 개의 프로세스로 구성하여 각 프로세스가 하나의 작업을 처리하도록 하는 것이다.

 

  • 프로세스 간 통신(IPC)은 별도의 복잡한 방법(파이프, 소켓 등)을 사용해야 한다.
  • Context Switching에서의 오버헤드
  • 프로세스 중 하나에 문제가 발생해도 다른 프로세스에 영향이 가지 않는다.
  • 프로세스 생성과 관리에 시간이 걸리고, 자원 소모가 크다.

 

 

✏️ 멀티 스레딩

 

: 하나의 응용프로그램을 여러 개의 스레드로 구성하고 각 스레드로 하여금 하나의 작업을 처리하도록 하는 것이다.

 

  • stack 영역을 제외한 모든 메모리를 공유하기 때문에 자원 공유와 통신이 빠르다.
  • 스레드 생성과 관리 비용이 적고, 메모리 사용량도 적다.
  • 자원 공유로 인한 동기화 문제(데드락, 레이스 컨디션등)가 발생할 수 있다.

 

더보기

🧐 동기화 문제란?

 

여러 스레드가 동시에 공유자원에 접근할 때, 데이터의 일관성이 깨지는 현상을 의미한다.

 

예를 들어,  스레드가 동시에 같은 변수의 값을 읽고 각각 1 더한  저장하면, 실제로는   더해야  값이  번만 더해질  있다. 이런 상황을 레이스 컨디션(Race Condition)이라고 하며, 동기화 문제가 대표적으로 발생하는 예다.

 

📌 iOS에서는 멀티 프로세싱과 멀티 스레딩 중에서 뭘 선호할까?

 

iOS는 일반적으로 단일 프로세스 모델을 사용한다고 한다. 

이 구조를 이해하려면 iOS의 샌드박스 정책을 알아야 한다.

 

샌드박스란, 각 앱이 자신의 파일, 네트워크 리소스, 환경설정, 하드웨어 등에만 접근할 수 있도록 제한하는 보호된 영역을 의미한다.
이렇게 함으로써 앱 간의 간섭을 방지하고, 보안 위험을 사전에 차단하며, 시스템의 일관성과 신뢰성을 높일 수 있다.

 

, iOS 기본적으로 하나의 앱이 하나의 샌드박스(=프로세스) 내에서만 동작하도록 설계되어 있다.
이런 구조 덕분에  앱은 독립적으로 실행되며, 다른 앱이나 시스템 전체에 영향을 주지 않는다.

 

자체 샌드박스 디렉토리 내에서 작동하는 iOS 앱

 

iOS의 샌드박스 정책과 시스템 구조상, 여러 프로세스를 사용하는 것은 자원 관리와 통신이 복잡해져 잘 사용하지 않는다.

결론적으로, 하나의 프로세스(샌드박스) 내에서 여러 스레드를 활용해 동시성(멀티스레딩)을 구현하는 것 일반적이다.

 

 


2. 멀티 스레딩이 필요한 이유는?

 

 

모바일 앱 개발에서는 동시 작업을 효율적으로 관리하는 것이 중요하다.

사용자는 앱이 많은 작업을 처리하는 상황에서도 즉각적인 반응을 기대한다. 하지만 앱 내부에서 데이터 로딩이나 복잡한 연산이 진행되고 있다는 사실을 사용자는 알 수 없기 때문에, 화면이 멈추거나 반응이 느려지면 자연스럽게 앱 사용을 중단할 수도 있다.

 

이런 이유로, 멀티스레딩은 필수적이다.

여러 작업을 동시에 실행함으로써, 앱은 사용자와의 상호작용(UI 처리)과 백그라운드 작업(데이터 로딩 등)을 효율적으로 분리하여 처리할 수 있다. 

 

, 사용자가 보고 있는 화면이 멈추지 않고 부드럽게 동작하도록 하려면, 멀티스레딩을 통해 동시 작업을 적절히 관리하는 것이 매우 중요하다.

 

📌 스레드의 종류

 

멀티스레딩 환경에서 주로 사용되는 스레드는 다음과 같이 나눌  있다.

 

1️⃣ 메인 스레드

 

: 주로 사용자 인터페이스(UI)를 담당

 

작업을 순차적으로 실행한다. 사용자 입력을 처리하고, 뷰를 업데이트하고, 애니메이션을 실행하는 등 즉각적인 피드백을 제공한다.

메인 스레드에서 장시간 작업이 수행되면 애플리케이션이 정지되어 사용자 경험이 저하될 수 있다.

 

 

2️⃣ 백그라운드 스레드

 

: 사용자와의 직접적인 상호작용 없이도 실행 가능한 작업을 처리

 

부하가 크거나 오래 걸리는 작업은 백그라운드 스레드에 할당하는 것이 좋다. 백그라운드 스레드는 데이터 가져오기나 집중적인 계산과 같은 작업을 기본 인터페이스에 영향을 주지 않고 수행할 수 있도록 할 수 있기 때문이다.

 

 

정리하자면, 메인 스레드는 즉각적인 사용자 피드백에 중점을 두는 반면, 백그라운드 스레드는 즉각적인 결과가 필요하지 않은 작업을 관리한다. 

 

 


3. GCD(Grand Central Dispatch)

 

iOS에서 멀티 스레딩을 쉽게 구현할 수 있도록 도와주는 GCD에 대해 알아보자.

 

📌 GCD란?

 

GCD(Grand Central Dispatch)는 iOS에서 동시성(멀티스레딩) 프로그래밍을 쉽게 구현할 수 있도록 Apple이 제공하는 프레임워크다. 스레드 관리와 실행에 대한 책임을 애플리케이션 레벨에서 운영체제 레벨로 넘겨버린다.

 

GCD의 핵심 목적은 여러 작업(Task)을 효율적으로 분산 처리하여, 앱의 성능과 반응성을 높이는 데 있다.

개발자는 직접 스레드를 생성하거나 관리할 필요 없이Dispatch Queue라는 대기열() 작업을 추가하기만 하면 된다.

 

Dispatch Queue GCD 제공하는 작업 대기열로, 여기에 작업을 넣으면 GCD 알아서 스레드에 배분해 실행한다.

당연히 Queue니까 FIFO 방식이다.

 

여러 블로그를 보면 GCD와 Dispatch Queue를 묶어서 설명할 때가 많은데...

정리하자면, Dispatch Queue와 GCD는 같은 의미가 아니라, Dispatch Queue GCD  구성 요소이며, GCD 사용할  가장 많이 활용하는 API라고 할 수 있다.

 

📌 GCD를 사용하지 않고 스레드를 직접 생성하면 안 되나?

 

GCD를 사용하지 않고 스레드를 직접 생성할 수도 있지만, 다음과 같은 문제가 있다.

 

 

1. 복잡성 증가

 

: 스레드를 직접 생성하고 관리하면 코드가 복잡해지고, 개발자가 스레드 생성, 종료, 동기화 같은 모든 부분을 신경 써야 한다.

 

2. 에러 발생 위험

 

: 직접 스레드 관리할 경우 레이스 컨디션, 데드락, 메모리 누수 같은 동시성 관련 버그가 발생하기 쉽다.

 

3. 메모리 관련 문제

 

: 수동으로 생성한 스레드는 자동 메모리 관리가 제대로 되지 않아 메모리 누수나 크래시로 이어질 수 있다. 

 

 

하지만 Dispatch Queue를 사용하면, 개발자의 부담이 줄어든다. 작업을 정의해서 Dispatch Queue에 넣기만 하면, 운영체제가 해당 작업들을 적절한 스레드에 할당해 실행한다. 따라서 개발자는 자신이 등록한 작업이 어떤 스레드에서 실행되는지 직접 신경 쓸 필요가 없다.

 

이처럼 GCD를 사용하면 시스템이 스레드 관리와 자원 할당을 자동으로 최적화해 주기 때문에, 대부분의 경우에 GCD를 사용하는 것이 효율적이라고 생각한다.

 

 

📌 DispatchQueue 특성 

 

1️⃣ Serial 

 

Serial은 순차적으로 한 번에 하나의 작업만 실행한다. 

 

let serialQueue = DispatchQueue(label: "serial")

serialQueue.async {
    print("작업 1")
}
serialQueue.async {
    print("작업 2")
}

 

코드를 실행하면 결과가 작업 1, 작업 2 순서대로 실행된다.

 

2️⃣ Current 

 

여러 작업을 동시에 실행해서 끝나는 순간이 보장되지 않는다.

 

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

concurrentQueue.async {
    print("작업 1")
}
concurrentQueue.async {
    print("작업 2")
}

 

작업 1과 작업 2가 동시에 실행되어서 출력 값이 [작업 1 -> 작업 2] 순서로 나오거나 [작업 2 -> 작업 1] 순서로 나올 수도 있다.

 

즉, 동시에 실행되어서 끝나는 순간을 보장할 수 없다.

 

 

📌 Queue 종류

 

1️⃣ Main Queue

 

Main Queue는 메인 스레드에서 작업을 보관하고 수행하는 큐다. 메인 스레드에서 동작하기 때문에 단 하나만 존재하고 Serial 특성을 가지고 있다.

메인 스레드에서는 UI 요소들을 많이 관리해야 한다. 사용자에게 직접적으로 보여지기 때문에 가장 빠르게 처리되어야 하기 때문이다. Main Queue가 막히면 앱이 멈추거나 강제 종료될 수 있으므로, 무거운 작업은 Main Queue에서 실행하지 않는 것이 중요하다.

 

DispatchQueue.main.async {
    // UI 업데이트 코드
}

 

2️⃣ Global Queue

 

메인 스레드가 아닌 다른 스레드에서 작업을 처리한다. 동시에 concurrent 특성을 가지기 때문에 여러 스레드로 작업이 분산되어 동시에 처리된다. 

여러 개의 Global Queue가 있으며, 각각 다른 우선순위(QoS)를 가질 수 있다. (대표적인 Qos: .userInteractive, .userInitiated, .default, .utility, .background)

 

주로 백그라운드 작업, 네트워크 통신, 데이터 처리 등 UI와 직접적으로 관련 없는 작업에 사용한다.

 

DispatchQueue.global(qos: .background).async {
    // 백그라운드 작업
}

 

 

3️⃣ Custom Queue

 

개발자가 직접 생성하는 큐로, Serial Queue 또는 Concurrent Queue를 만들 수 있다. 

 

  • Serial Queue: 한 번에 하나의 작업만 순서대로 실행
  • Concurrent Queue: 여러 작업을 동시에 실행 가능
// 직렬 큐
let serialQueue = DispatchQueue(label: "serialQueue")

serialQueue.async {
    print("이 작업은 직렬 큐에서 실행됩니다.")
}

// 동시 큐
let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)

concurrentQueue.async {
    print("이 작업은 동시 큐에서 실행됩니다.")
}

 


출처

 

https://jeonyeohun.tistory.com/279

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함