일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- swiftUI
- ribs
- collectionview
- Protocol
- swift documentation
- Clean Code
- Refactoring
- UICollectionView
- SWIFT
- UITextView
- 리펙토링
- RxCocoa
- rxswift
- 애니메이션
- Human interface guide
- 스위프트
- tableView
- 클린 코드
- Xcode
- clean architecture
- 리팩토링
- HIG
- ios
- map
- combine
- uitableview
- uiscrollview
- Observable
- MVVM
- 리펙터링
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 4. Concurrent Programming - Thread Safe Array 구현방법 (DispatchQueue의 barrier 사용) 본문
[iOS - swift] 4. Concurrent Programming - Thread Safe Array 구현방법 (DispatchQueue의 barrier 사용)
jake-kim 2022. 8. 14. 23:401. Concurrent Programming - NSLock, DispatchSemaphore 사용 방법
2. Concurrent Programming - DispatchSemaphore로 코틀린의 CompletableDeferred 구현방법
3. Concurrent Programming - DispatchQueue의 serial, concurrent, async, sync 이해하고 사용하기
4. Concurrent Programming - Thread Safe Array 구현방법 (DispatchQueue의 barrier 사용)
5. Concurrent Programming - OperationQueue로 동적으로 작업 추가, 취소하는 모듈 구현방법
* 해당 글을 이해하기 위해서는 이전 포스팅 글(DispatchQueue의 serial, concurrent, async, sync 이해하고 사용하기) 이해가 먼저 필수
기본 지식) DispatchQueue, sync와 async의 우선순위
- sync, async는 이전 포스팅 글에서 알아본 대로, 해당 코드를 사용하는 스레드에 적용
- 메인 스레드에서 async, sync하게 처리하는 작업이 여러개 존재할 때 우선순위는 async보다 sync가 높음
- async는 결과가 끝나는 것을 기다리지 않고 다음 작업을 수행하여, 다른 작업에도 기능을 수행하여 현재 작업에 100%를 집중하지 않아 sync보다 결과를 늦게 반환하는 경우가 존재
// 짝수는 모두 sync로 한 결과, 홀수보다 짝수 print가 더 빨리 실행되는 결과
let serialQueue1 = DispatchQueue(label: "serial1")
let serialQueue2 = DispatchQueue(label: "serial2")
serialQueue1.async {
print("job 1")
}
serialQueue2.sync {
print("job 2")
}
serialQueue1.async {
print("job 3")
}
serialQueue2.sync {
print("job 4")
}
serialQueue1.async {
print("job 5")
}
serialQueue2.sync {
print("job 6")
}
serialQueue1.async {
print("job 7")
}
serialQueue2.sync {
print("job 8")
}
/*
job 2
job 1
job 4
job 6
job 8
job 3
job 5
job 7
*/
- async보다 sync가 결과를 더 빠르게 내놓으므로 이 것을 활용하여 Thread safe 배열을 만들때, 읽기에는 곧바로 결과를 리턴해주는 sync를 사용하고, 쓰기에는 느리게 쓰여져도 되므로 async로 구현
Serial Queue에서의 Thread Safe Array 구현 아이디어
* Thread Safe: 어떤 함수나 변수 또는 객체가 멀티스레딩 환경에서 동시에 접근을 하더라도 프로그램 실행에 문제가 없음
* 상호 배제(mutual exclusion, Mutex): 동시성 프로그래밍에서 공유 불가능한 자원의 동시 사용을 피하기 위해 사용되는 방법
- Serial Queue에서의 Thread Safe 구현
- serial 큐에서는 작업이 쌓여있어도, 큐에서 스레드로 보낸 작업이 끝나기 전까지는 큐에서 스레드로 다음 작업을 빼내지 않으므로 여러 스레드가 일을 처리하는 일이 없으므로, 특정 처리를 해주지 않아도 Thread safe함
- 위에서 알아본 성능을 위해서 read에는 sync로, write에는 async로 접근
let serialQueue = DispatchQueue(label: "serial queue")
serialQueue.async() {
// write
}
serialQueue.sync() {
// read
}
Concurrent Queue에서의 Thread Safe Array 구현 아이디어
- concurrent 큐에 작업이 쌓여 있으면 스레드로 작업을 한꺼번에 여러개를 보내는데, 이 때 write작업을 동시에 하게되면 memory conflict가 발생 (read할때는 값이 변경되지 않으므로 상관 x)
- barrier라는 플래그를 사용하여 해결
- barrier 플래그는 단순히 마치 serial하게 동작하도록 하는 것 (barrier 큐에 들어간 작업들은, 스레드로 작업을 보낼 때 하나씩만 보내며 이전 작업이 끝났을때만 다음 작업을 전송)
let concurrentQueue = DispatchQueue(label: "concurrent queue")
concurrentQueue.async(flags: .barrier) {
// write
}
concurrentQueue.sync() {
// read
}
Thread Safe Array 구현
- 동시성 작업이 필요한 경우, serial queue보단 concurrent queue가 성능이 더 좋을 것이므로 concurrent queue를 사용
- 성능을 위해 read할땐 sync로, write할땐 async로 접근
- concurrent queue를 사용하면서의 예상되는 문제점은 write할때 async로 접근할때 스레드 세이프하지 않을수 있지만, barrier를 사용하면 해결가능
final class ThreadSafeArray<T> {
private var array = [T]()
private let queue = DispatchQueue(label: "Thread Safe Array", attributes: .concurrent)
}
// Operator
extension ThreadSafeArray {
func first() -> T? {
queue.sync {
self.array.first
}
}
func removeLast() {
queue.async(flags: .barrier) {
if !self.array.isEmpty {
self.array.remove(at: self.array.count - 1)
}
}
}
}
* 전체 코드: https://github.com/JK0369/ExThreadSafeArray
* 참고
https://ko.wikipedia.org/wiki/%EC%83%81%ED%98%B8_%EB%B0%B0%EC%A0%9C
https://zamzam.io/creating-thread-safe-arrays-in-swift/