Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- swiftUI
- tableView
- rxswift
- 스위프트
- 리펙터링
- map
- 애니메이션
- UICollectionView
- Observable
- SWIFT
- uiscrollview
- Human interface guide
- uitableview
- 클린 코드
- Xcode
- MVVM
- 리팩토링
- Protocol
- ribs
- ios
- Refactoring
- UITextView
- 리펙토링
- Clean Code
- swift documentation
- RxCocoa
- clean architecture
- HIG
- collectionview
- combine
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 1. Concurrent Programming - NSLock, DispatchSemaphore 사용 방법 본문
iOS 응용 (swift)
[iOS - swift] 1. Concurrent Programming - NSLock, DispatchSemaphore 사용 방법
jake-kim 2022. 8. 11. 22:011. 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로 동적으로 작업 추가, 취소하는 모듈 구현방법
NSLock 개념
(자세한 ThreadSafe개념과 NSLock 개념은 이곳 참고)
- NSLock은 이름에서 알 수 있듯이, Objective-C에서부터 만들어졌고 Thread safe하게 관리해주는 역할
- NSLock의 가장 단점은 NSLock을 해제할 때 같은 스레드에서만 해제해야 잠금이 해재됨
(애플 공식 문서에서도 빨갛게 Warning으로 적혀있는 부분)
- NSLock의 가장 단점은 NSLock을 해제할 때 같은 스레드에서만 해제해야 잠금이 해재됨
- NSLock 사용방법
let lock = NSLock()
lock.lock()
// this is critical section
// processing some task...
lock.unlock()
- NSLock을 잘못쓰는 경우1
- lock()을 호출한 곳이 global()안의 스레드지만, unlock()을 한곳이 main이므로 unlock동작이 안됨
- NSLock은 반드시 lock()을 호출한 스레드에서 unlock()을 호출해야 동작
func runWrongFirstNSLock() {
let lock = NSLock()
DispatchQueue.global().async {
lock.lock()
}
print("wait for task A")
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
print("finish Task A")
lock.unlock()
}
print("Call after task A is done")
}
- NSLock을 잘못쓰는 경우2
- global()로 선언해서 같은 큐이지만, DispatchQueue에는 여러개의 쓰레드가 동작하므로 lock할때와 unlock할때의 쓰레드가 다를 수 있음
func runWrongSecondNSLock() {
let lock = NSLock()
DispatchQueue.global().async {
lock.lock()
}
print("wait for task A")
DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
print("finish Task A")
lock.unlock()
}
print("Call after task A is done")
}
DispatchSemaphore 개념
(자세한 DispatchSemaphore 개념은 이곳 참고)
- 세마포어를 관리하는 API이며, 세마포어 값을 초기화하고, wait()와 signal()을 이용하여 스레드 작업 관리 제어
- wait(): 자원 점유 (-1)
- signal(): 자원 종료 (+1)
let semaphore = DispatchSemaphore(value: 2)
semaphore.wait()
// 스레드 2개 진입 가능
// processing some task...
semaphore.signal()
- NSLock과의 차이점
- NSLock은 lock을 수행한 스레드에서 unlock을 수행해야 적용
- DispatchSemaphore는 wait()를 수행한 스레드가 아닌, 다른 스레드에서 signal()을 수행해도 적용
- DispatchSemaphore의 기능 2가지
- 세마포어의 값을 0보다 크게 설정 - 동시에 접근할 수 있는 스레드의 갯수 지정 기능
- 세마포어의 값을 0으로 설정 - blocking 기능 (해당 작업이 끝날때까지 기다리고 나서 작업을 수행시키고 싶은 경우)
- 세마포어에서의 핵심은 wait()를 호출하는 스레드가 중요 (메인 스레드에서 호출하면 UI가 멈춘상태가 되므로 주의)
세마포어의 값을 0보다 크게 한 경우 예시)
- 세마포어 value를 2로 설정하여, 스레드가 2개씩 들어가면서 forEach문 내부에 1초마다 2개씩 작업
func runTwoSemaphore() {
let semaphore = DispatchSemaphore(value: 2)
(1...10).forEach { i in
DispatchQueue.global().async() {
semaphore.wait() //semaphore 감소
print("Entry critcal section \(i)")
sleep(1)
print("Exit critcal section \(i)")
semaphore.signal()
}
}
}
세마포어의 값을 0으로 한 경우 예시 - block기능)
- 잘못 사용한 경우1
- 세마포어는 main thread에서 부르게 되면 UI도 같이 동작 wait하므로 MainThread에서 wait 금지
- 이 메소드를 viewDidLoad에서 호출하게되면 main thread로 인식하고 아래 semaphore.wait()도 메인 스레드이므로 메인스레드가 block되면서 UI가 그려지지 않는 현상이 존재
func runWrongFirstZeroSemaphore() {
let semaphore = DispatchSemaphore(value: 0)
print("wait for task A")
DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
print("finish Task A")
semaphore.signal()
}
// semaphore가 0이라 task A 종료까지 block
semaphore.wait()
print("Call after task A is done")
}
- 잘 사용한 경우1
- DispatchQueue.global()를 사용하면 안에서 스레드를 새로 생성하므로, 다른 global queue의 스레드와 겹치지 않게 되어, wait()되어도 다른곳에서 global로 접근해도 block되지 않고 사용이 가능
func runGoodZeroSemaphore() {
let semaphore = DispatchSemaphore(value: 0)
print("wait for task A")
DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
print("finish Task A")
semaphore.signal()
}
// semaphore가 0이라 task A 종료까지 block
DispatchQueue.global().async {
semaphore.wait()
print("Call after task A is done")
}
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
print("Test - can call before task A done")
}
}
/*
wait for task A
Test - can call before task A done
finish Task A
Call after task A is done
*/
- 잘 사용한 경우2
- DispatchQueue.global()로 주지 않고 아예 별도의 스레드 안에서 wait()하게 하여 해당 스레드만 wait가 적용되게끔 하는 방법
func runBestBlockSemaphoreUsingThread() {
let semaphore = DispatchSemaphore(value: 0)
let thread = Thread {
print("wait for task A")
// wait 하는 곳의 스레드가 wait되므로 Thread로 따로 빼야 효율적
// 만약 DispatchQueue.global()에서 wait하면 global()에 사용되는 스레드들이 wait되어서 비효율적
semaphore.wait()
print("Call after task A is done")
}
Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in
print("finish Task A")
semaphore.signal()
}
thread.start()
// semaphore와 연관없는 global() 큐
DispatchQueue.global().async {
print("바로 실행되어야함")
}
DispatchQueue.global().asyncAfter(deadline: .now() + 10) {
thread.cancel()
}
}
* 전체 코드: https://github.com/JK0369/ExLockAndSemaphore
* 참고
'iOS 응용 (swift)' 카테고리의 다른 글
Comments