티스토리 뷰
[iOS] GCD 심화 - 동시성과 관련된 문제들 (Race condition, Tsan, DispatchQueue barrier, Thread-safe, Deadlocks, Priority Inversion)
Peppo 2022. 4. 8. 20:53경쟁 상황 (Race Condition)
두개 이상의 Thread를 사용하면서, 동일한 메모리 접근 등으로 인해 발생할 수 있는 문제
아래 예제를 보시죠.
var value = 777
func changeValue1() {
sleep(2)
value = 222
}
func changeValue2() {
sleep(2)
value = 0
}
queue.async {
changeValue1()
}
queue.async {
changeValue2()
}
print("(비동기)함수 실행값:", value)
print에는 뭐가 찍힐까요 ?
777 이나와요.
changeValue1,2 함수가 실행되고 기다리는동안 (sleep)
제일 아래 print 문이 실행되는거죠.
해결방법 (3가지)
1. TSan (Thread Sanitizer) : 앱 출시전 반드시 해야할 일
Xcode - ⌘(cmd) - ' < ' - Run - Diagnostics - Thread Sanitizer 체크
후에 문제 되는 부분을 찾아서 해결 합니다.
NOTE
Tsan 모드로 실행하면 빌드시간이 오래걸리니 경쟁상황을 해결하면 체크해제 할것
2. Serial Queue + Sync (엄격한 *Thread-safe)
sync 메소드는 main thread가 아닌곳에서 사용가능 하며, 잠재적 경쟁상황을 피하는데 유용합니다.
예제
let serialQueue = DispatchQueue(label: "serialQueue")
DispatchQueue.global().async {
serialQueue.sync { // async를 사용하게되면 _count 값을 못받아올 수도 있음.
_count
}
_count
}
Thread -Safe
- 여러 Thread가 동시에 쓰여도 안전하다.
- 데이터에 여러 Thread를 사용하여 접근해도, 한번에 한개의 Thread만 접근하도록 처리하여 경쟁상황의 문제없이 사용.
GCD 강의 - SerialSyncProject - ViewController 참고
3. 디스패치 배리어 작업 (Serial Queue + Sync 보다 효율적)
concurrent 큐 내의 여러개 Thread 중에서 '배리어 작업'의 경우,
한개의 Thread만 사용해 serial(직렬)로 실행가능한 방법.
예시
// Person.swift
import Foundation
open class Person {
private var firstName: String
private var lastName: String
public init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
// 이름 바꾸는 메서드
open func changeName(firstName: String, lastName: String) {
randomDelay(maxDuration: 0.2)
self.firstName = firstName
randomDelay(maxDuration: 1)
self.lastName = lastName
}
// 이름 Get계산속성
open var name: String {
return "\(firstName) \(lastName)"
}
}
class BarrierThreadSafePerson: Person {
let newConcurrentQueue = DispatchQueue(label: "com.inflearn.person.newConcurrent", attributes: .concurrent)
// 🎾 쓰기 - 동시 + 배리어(Barrier) 작업으로 설정
override func changeName(firstName: String, lastName: String) {
newConcurrentQueue.async(flags: .barrier) { // flags: .barrier 사용
super.changeName(firstName: firstName, lastName: lastName)
}
}
// 🎾 읽기 - 동시 + 동기(sync) 작업으로 설정
override var name: String {
newConcurrentQueue.sync {
return super.name
}
}
}
아래 그림을 참고하자면,
read(읽기) 작업들은 동시에 실행하게 두되,
write(쓰기) 작업이 진행 될때는 다른 작업을 멈추고,
쓰기작업이 완료되면 멈춰있던 작업들이 재 실행 됩니다.
NOTE
읽기 작업은 동시에 이루어져도 괜찮지만,
읽기/쓰기 또는 쓰기 작업이 동시에 이루어 지게 되면 Thread-safe 하지 않은 상황이 됩니다.
GCD 강의자료 7-3 참고
교착상태 (Deadlocks)
한정된 자원을 2개 이상의 Thread가 서로 점유하려고 하면서 자원사용이 막히는 상태.
해결방법
- Serial Queue로 해결 가능
단, 세마포어 같은 제한된 리소스 순서가 있는건 조심해서 사용해야 함.
우선 순위의 뒤바뀜 (Priority Inversion)
다양한 경우에서, 작업의 Qos가 뒤바뀌어서 작업이 진행되는 경우.
- Serial Queue에서 높은 우선순위 작업이 낮은 우선순위의 뒤에 보내지는 경우
- 낮은 우선순위의 작업이 높은 우선순위가 필요한 자원을 잠그고 있는 경우 (세마포어 등)
- 높은 우선순위 작업이 낮은작업에 의존하는 경우 (Operation)
해결방법
GCD가 우선순위를 자동으로 조정해서 해결.
참고
'iOS' 카테고리의 다른 글
[iOS] 스토리 보드 없이 코드로 UI작업할 때 세팅 (no storyboard setting/ Code based UI) (0) | 2022.04.13 |
---|---|
[Swift] 상속 (Inheritance) (0) | 2022.04.10 |
[iOS] 메모리 구조 (memory) (0) | 2022.04.06 |
[iOS] LaunchScreen 설정 후 디바이스에만 나오지 않을때 (0) | 2022.04.05 |
[Swift] Subscripts (서브스크립트) (0) | 2022.04.01 |
- Total
- Today
- Yesterday
- Swift 내림차순
- Class
- RTCCameraVideoCapturer
- swift (programmers)
- Swift final
- Swift init
- Swift RIBs
- Swift ModernRIBs
- Swift
- Swift inout
- Swift 프로그래머스
- swift 고차함수
- swift property
- Combine: Asynchronous Programming with Swift
- Swift Leetcode
- Swift joined
- swift reduce
- swift programmers
- removeLast()
- Swift 알고리즘
- Swift 프로퍼티
- Swift Error Handling
- ios
- iOS error
- 2023년 회고
- Swift joined()
- 원티드 프리온보딩
- swift protocol
- CS 네트워크
- RIBs tutorial
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |