일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 3b52.1
- 리액트
- privacyinfo.plist
- 리액트 네이티브
- react-native-fast-image
- native
- Android
- 뷰 생명주기
- SwiftUI
- panorama view
- launch screen
- 360도 이미지
- data driven construct
- 앱 성능 개선
- react
- React-Native
- launchscreen
- 명시적 정체성
- React Native
- ios
- 라이브러리 없이
- ssot
- 뷰 정체성
- 스켈레톤 통합
- 네이티브
- 파노라마 뷰
- 구조적 정체성
- 360도 뷰어
- requirenativecomponent
- 360도 이미지 뷰어
- Today
- Total
Neoself의 기술 블로그
iOS에서의 동시성 프로그래밍 본문
1. 동시성
동시성 프로그래밍은 엄밀히 따지면, 동시성과 병렬성을 모두 포함하는 개념이라고 볼 수 있습니다. 즉, 여러 작업을 동시에 처리하는 프로그래밍 방식입니다.
여기서 동시성 프로그래밍(Concurrent Programming)과 병렬성 프로그래밍(Parrallel Programming)의 가장 큰 차이는 무엇일까요?
둘다 여러 작업을 동시에 처리한다는 방향을 공유하고 있지만, 동시성이 한번에 하나의 작업을 번갈아가며 처리를 하는 방식이라면, 병렬성은 멀티코어 환경을 바탕으로 여러작업을 실제로 동시에 처리하는 방식을 일컫습니다.
즉 동시성 프로그래밍은 소프트웨어적 쓰레드에서 동시에 일을 하는 개념이기 때문에, 보다 개발자 개입이 중요한 처리방식입니다.
아래 다이어그램을 보면 동시성과 병렬성의 동작방식 차이를 더 쉽게 이해할 수 있을 것 같습니다! 코어가 1개일 경우에는, 각 작업을 주로 시분할의 원리에 따라 쪼갠 후에, 각 작업을 번갈아서 수행하는 과정을 거치게 됩니다. 하지만, 멀티코어의 경우 각 쓰레드에 일괄적으로 할당이 가능하기 때문에 번갈아서 수행하는 과정없이, 말 그대로 동시에 여러 작업을 처리하게 됩니다.
동기와 비동기의 차이점
작업의 완료를 기다리느냐 입니다.
동기는 작업의 완료가 끝날때까지 호출 스레드가 블록되어, 작업 완료까지 다음 코드 실행이 되지 않습니다.
비동기로 처리할 경우 작업 완료여부와 상관없이, 다른 스레드로 작업을 할당시킨 후 호출 스레드에서는 다음 코드를 이어 실행하게합니다.
직렬큐와 Concurrent큐의 차이
작업의 실행순서입니다.
직렬 큐는 한번에 하나의 작업만 실행해 작업 순서가 보장되지만, 병렬 큐는 여러 스레드에 작업들을 할당시켜, 동시에 여러 작업을 수행할 할 수 있도록 합니다.
단일큐에 비동기 작업 할당할 경우,
비동기, 동기와 직렬큐, 병렬 큐로 4가지 조합 모두 가능하나, 동기+병렬은 호출 스레드가 블록돼, 작업 완료까지 다음 코드 실행이 되지 않기 때문에 동시성 이점을 얻지 못합니다. 따라서, 결과적으로 순차적 실행과 비슷한 패턴이 됩니다.
예전에는 Thread 객체를 직접 생성해 할당해줄 수 있었음.
하지만 높은 난이도로, 이를 추상화해 스레드에 작업을 쉽게 할당시키고자 GCD가 나왔다.
스레드: 작업을 실행하는 주체
큐: 작업을 저장하는 단위
iOS에서 비동기 작업 처리하는 방법
Completion Handler: 비동기 작업 완료 후 호출되는 콜백함수를 통해 완료된 작업을 처리하는 방식
@escaping 클로저 사용 필요하며, 참조 타입이기 때문에 weak self 캡처 리스트를 통해 메서드 호출 시에 Referencce Counting을 신경써줘야합니다.
피라미드 지옥이 쉽게 발생될 수 있습니다
Combine, RxSwift
비동기 작업 시퀀스를 처리하는 기능 제공합니다. 비동기 작업 결과를 스트림으로 처리하고, 다양한 연산자를 통해 데이터 조작 및 변환합니다.
Publisher와 Subscriber(Observable과 Subscriber) 기반으로 동작하기 때문에 선언적인 방식으로 데이터를 조작할 수 있으며, 순수 함수 형태인 연산자들을 chaining해 조작한다는 점에서 함수형 프로그래밍 개념을 활용한 비동기작업 처리방식입니다.
Modern Concurrency (Swift 5.5+)
동기적인 코드와 유사한 코드로 비동기 작업을 처리할 수 있도록 합니다. 하지만, 내부 동작 과정에서 추가적인 메모리 사용이 필요하다는 단점이 있다.
Semaphore, Mutex
두 방식 모두 동기화 메커니즘을 제공하고 공유 자원에 대한 접근을 제어하기 위한 방식입니다.
*둘 다 멀티스레딩 환경에서 공유 자원 접근을 동기화하기 위한 동기화 매커니즘을 의미합니다.
Critical Section: 한 번에 둘 이상의 스레드가 접근하면 안되는 공유 자원 영역(클래스 내부 관리 변수에 대한 set 함수)
Mutex(Mutual Exclusion)
Critical Section을 가진 스레드들의 Running Tme이 서로 겹치지 않게 단독으로 실행되게 하는 알고리즘
-> 뮤텍스 객체는 두 스레드가 동시 사용 불가
*공유 리소스에 대한 접근을 조율하기 위해 locking과 unlocking 사용
*NSLock 메서드가 있음
Semaphore
공유 자원에 진입할 수 있는 허용 개수를 의미하는 정수 변수이며, 0이 되면 공유 자원에 진입하는 것을 막음
*DispatchSemaphore(value:Int): 공유자원에 접근 가능한 작업 수를 제한하는
*wait(): semaphore 값이 0보다 크면 1 감소시키며, 0이면 값 증가까지 대기
*signal(): semaphore 값을 1 증가시킴
두 동기화 매커니즘의 차이
1. Mutex는 동기화 대상이 1개일때에만 사용하지만, Semaphore는 동기화 대상이 2개 이상일때 사용합니다.
2. 세마포어는 뮤텍스가 될 수 있지만, 뮤텍스는 세마포어가 될 수 없다.
Mutex는 0, 1로 이루어진 이진 상태를 가지므로, Binary Semaphore이라고도 불린다.
3. 뮤텍스는 자원 소유가 가능하고 책임을 갖는다.
Semaphor 는 자원을 소유하지 못한다.
NSLock
Mutex를 기반으로 구현된 Objective-C/Swift의 락 클래스입니다. 내부적으로 pthread_mutex를 래핑
하나의 스레드 혹은 둘 이상의 스레드가 lock()이 있는 다음 코드 부분을 접근하지 못하도록 상호배제하는 방법
critical section에 들어갈때 lock()을 걸고 작업을 끝내면 unlock() 호출하는 방법
NSRecursiveLock
둘 이상의 스레드가 lock()이 있는 다음 코드 부분을 접근하지 못하도록 상호배제하는 방법
NSLock과 NSRecursiveLock 차이
NSRecursiveLock는 A 스레드가 lock을 걸었을 때, A 스레드가 다시 해당 부분에 접근할 때 lock을 다시 걸을 수 있음
같은 스레드에서는 lock을 걸 필요가 없을때는 NSRecursiveLock을 사용할 것
뮤텍스와 세마포어 둘다 데이터 무결성을 보장할 수 없으며 모든 교착 상태를 해결하지는 못한다는 한계점이 있다. 하지만 상호 배제를 위한 기본적인 기법이며, 여기에 조금 더 복잡한 매커니즘을 적용해 개선된 성능을 가질 수 있도록 하는것이 가장 중요한 프로그래밍 방식이 될 것이다.
GCD(Grand Central Dispatch)
애플이 개발한 동시성 프로그래밍을 위한 저수준 API입니다
개발자가 작업을 Dispatch Queue에 제출하면, GCD는 시스템 리소스 상황에 따라 사용 가능한 스레드에 제출받은 작업들을 할당해줍니다.
작업을 큐에 제출하고 시스템이 관리하는 스레드 풀에서 제출된 작업을 실행합니다.
글로벌 큐와 메인 큐의 차이점
시리얼큐, 컨큐런트큐
메인 큐(Interface Thread)
UI 작업을 처리하는 단일 스레드 큐(Serial Queue라서, 한번에 한 개의 Task밖에 실행하지 못함)
모든 UI 업데이트는 반드시 메인 큐에서 이루어져야 합니다.
글로벌 큐(Background Thread)
시스템이 관리하는 동시성 큐로, QoS(Quality of Service) 우선순위에 따라 다양한 레벨이 있습니다.
Framework들은 모두 Background에서 구동
커스텀 큐를 만들어야하는 목적
레이블 전달하여 큐가 무엇인지 명시하여 작업 순서 보장하기 위해 사용
DispatchGroup 사용 시, 관련 작업이 끝나는 시점을.
메인 스레드에서 실행되는 ViewController에서 DispatchQueue.main.sync 실행 시, 데드락 발생
왜?!
왜 메인스레드에서 UI 실행해야하는지?
RunLoop 동작 아래에서 실행되기 때문에, 충돌 가능성 있음.
DispatchWorkItem
GCD(Grand Central Dispatch)에서 큐에 제출할 작업을 캡슐화하는 클래스입니다.
특히 작업의 의존성을 관리하거나, 작업을 지연 실행하거나 취소해야 할 때 자주 사용됩니다.
// MARK: - 1. 기본 사용법
let workItem = DispatchWorkItem {
// 실행할 작업
}
// 비동기 실행
DispatchQueue.main.async(execute: workItem)
// 지연 실행
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: workItem)
// MARK: - 2. 작업 취소
let workItem = DispatchWorkItem {
// 긴 작업
}
workItem.cancel()
// 취소 여부 확인
if workItem.isCancelled {
// 취소 처리
}
// MARK: - 3. 작업 완료 알림
workItem.notify(queue: .main) {
// 작업 완료 후 실행할 코드
}
// MARK: - 4. 의존성 설정
let firstItem = DispatchWorkItem { }
let secondItem = DispatchWorkItem { }
// firstItem 완료 후 secondItem 실행
firstItem.notify(queue: .main, execute: secondItem)
DispatchAutoReleaseFrequency: 방출 빈도값을 설정하는 객체 - workItem 실행될때마다 해제
WorkItem이 속한 개념
https://developer.apple.com/documentation/dispatch/dispatchqueue/autoreleasefrequency
'개발지식 정리 > CS정리' 카테고리의 다른 글
iOS CS 정리(241219) (0) | 2024.12.12 |
---|---|
CS 정리 (10월 30일) (0) | 2024.10.30 |
CS 정리 (10월 22일) (0) | 2024.10.22 |