본문 바로가기
Swift

[iOS] About 동시성

by dsungc 2024. 11. 10.

 

동시성

여러 작업을 동시에 실행되는 것 처럼 보이게 하는 것

 

 

프로그램 / 프로세스 / 스레드

 

프로그램

- 단순한 코드 덩어리

 

프로세스

- 프로그램이 실제로 실행되어 메모리에 올라가 실행 중인 것

 

스레드

- 프로세스 내에서, 프로세스의 자원을 이용해서 실제로 작업을 수행하는 것

 

 

멀티 프로세스

멀티 프로세스

- 하나의 운영체제에서 동시에 프로세스를 여러개 실행시킬 수 있는 기술

 

멀티 프로세스 환경에서의 메모리 구조

- 하나의 운영체제에서 여러 프로세스들이 각각의 독립적인 공간을 할당받음

- 하나의 프로세스 내부에 코드, 데이터, 힙, 스택 영역들이 각각 존재

 

 

멀티 스레드

멀티 스레드

- 하나의 프로세스 내에서 둘 이상의 스레드가 동시에 작업을 수행하는 것

 

멀티 스레드 환경에서의 프로세스의 메모리 구조

- 하나의 운영체제에서 여러 프로세스들이 각각의 독립적인 공간을 할당받음

- 하나의 프로세스 내부에 코드, 데이터, 힙, 스택 영역들이 각각 존재

- 코드, 데이터, 스택 영역은 컴파일 시 메모리 크기가 결정됨  >> 정적할당

- 힙 영역은 런타임 시 크기가 결정됨  >> 동적할당

 

- 코드, 데이터, 스택의 경우 메모리 주소값이 낮은 주소값부터 할당

- 스택의 경우 높은 주소값부터 할당맏음

- 힙과 스택 영역은 서로 공유

 

- Stack 영역의 메모리 주소값이 지나치게 할당되어 Heap 영역을 침범하는 것을 Stack Overflow

- Heap 영역의 메모리 주소값이 지나치게 많이 할당되어 Stack 영역을 침범하는 경우엔 Heap Overflow

 

 

동시성 이슈

- 한 프로세스 내에서 여러 스레드들이 동시에 동작하면서 발생하는 문제들

- Race Condition, Deadlock, Starvation, Priority Inversion 등

 

1. Race Condition

여러 스레드들이 같은 공유 자원(데이터)에 접근해서 결과값에 영향을 줄 수 있는 상태

 

1-1. 해결방법

 

상호배제를 통해 공유 자원에 하나의 스레드만 접근할 수 있도록 설계

 

1-2. Mutex

 

- 공유 자원에 접근하는 스레드들에게 lock이라는 bool type의 변수를 통해 권한을 주고, lock을 취득한 스레드만 자원에 접근할 수 있도록 하는 방법

 

- 위의 스레드들은 lock이 해제될 때까지 반복문을 통해 기다리는데 이를 Spinlock 이라고함

 

Spinlock 

 

- 대기 순서 보장 X

- 결국 공유자원에 접근하지 못하는 스레드가 발생할 가능성이 있기에 동시성 이슈인 Starvation 문제 발생 가능

 

1-2. Semaphore

- 공유자원에 접근할 수 있는 스레드의 개수를 Semaphore라는 Interger 변수로 관리하는 기법

- Semaphore가 0 --> 공유 자원에 더이상 접근 불가능, 접근하지 못하는 스레드들은 대기큐에서 관리  --> Starvation 문제 XX

- Semaphore가 1 --> 공유자원에 하나의 스레드만 접근하기 때문에 문제 일어나지 않음

- Semephore가 2 이상 --> 결국 여러 스레드가 하나의 공유 자원에 접근해서 값을 변경할 수 있기 때문에 Race Condition 문제 발생

 

 

1-3. Binary Semaphore

Semaphore 특성은 그대로 가져가되, Semephore를 이진수의 Binary 형태로 관리하기 때문에 스레드가 한 개만 접근이 가능, 고로 Race Condition 문제가 일어나지 않음

 

 

2. Deadlock

교착 상태, 두 개 이상의 작업이 서로 끝나기만을 기다리는 상태

 

2-1. Deadlock 발생 조건

 

상호배제

공유자원에 하나의 스레드만 접근 가능해야함

 

점유대기

자원을 하나 보유한 상태에서, 다른 스레드에 할당된 자원을 점유하기 위해 대기하는 상태

 

비선점

다른 스레드에게 할당된 자원을 강제로 빼앗을 수 없음

 

순환대기

대기하는 스레드들의 집합이 순환형태로 자원을 대기

 

2-2. Deadlock 해결 방법

 

예방

데드락 발생 조건 중 1개를 발생시키지 않도록 설정

 

회피

은행원 알고리즘을 통해 데드락 발생 가능성에 대해 예측하고, 발생 가능성이 있다고 하면 요청 거절

 

탐지 / 복구

- Deadlock 허용하지만, 데드락 발생 여부를 지속적으로 탐지

- 데드락이 발생하면 Deadlock Recovery 방식을 통해 Deadlock을 회복

 

무시

- 아무처리 X

- 해결하는 데 드는 Resource가 크기 때문에, 무시를 제일 많이 채택

 

동시성 이슈 해결 방법

 

1. GCD

GCD에 대한 게시글

 

1.1 DispatchQueue.sync

 

sync 보낼 시, Thread 1개가 보장되므로 해결 가능

 

1.2 Dispatch Barrier

 

- 병렬 실행큐에서 작업을 순차적으로 실행하도록 보장, 이전 작업이 완료될때까지 다른 작업 차단

- 읽기 작업과 쓰기 작업을 분리해 동시성 제어

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

concurrentQueue.async {
    print("읽기 작업 1")
}
concurrentQueue.async {
    print("읽기 작업 2")
}
concurrentQueue.async(flags: .barrier) {
    print("쓰기 작업 - 순차적으로 실행")
}

 

1.3 NSLock

 

- Mutex 와 동일한 기법으로, 앱 내에서 여러 실행 스레드의 작업 조정하는 객체

- lock(berfoer: Date:) : 특정 시점까지 lock을 획득하도록 시도하는 메서드

- unlock() : lock을 해제하여 다른 스레드가 접근할 수 있도록 설정

- try() : lock을 획득할 수 있는지 시도하는 메서드

let lock = NSLock()

func criticalSection() {
    lock.lock()
    // 보호할 코드
    print("Thread-safe 작업 실행")
    lock.unlock()
}

 

 

1.4  DispatchSemaphore

 

- semaphore와 동일한 기법, 공유자원에 접근할 수 있는 스레드 개수 지정

- wait() : semaphore 변수를 감소 메서드

- signal() : semaphore 변수 증가 메서드

let semaphore = DispatchSemaphore(value: 1)

DispatchQueue.global().async {
    semaphore.wait() // 리소스 접근 허용 대기
    print("Thread-safe 작업 실행")
    semaphore.signal() // 리소스 해제
}

 

 

2. Swift Concurrency

이 친구는 이후 포스트에 적어보겠습니다~

Coming Soon ~

 

.

.

.

.

'Swift' 카테고리의 다른 글

[iOS] About 채팅 UI  (0) 2024.11.20
[iOS] About Local Notification  (0) 2024.11.13
[iOS] About WidgetKit - 2(App Group)  (0) 2024.11.02
[iOS] About TDD  (1) 2024.10.30
[iOS] About Notification & Delegation  (0) 2024.10.26