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 |
Tags
- Refactoring
- tableView
- map
- Protocol
- UICollectionView
- swift documentation
- 애니메이션
- MVVM
- uiscrollview
- 스위프트
- Observable
- swiftUI
- ios
- HIG
- 리팩토링
- Xcode
- ribs
- RxCocoa
- 리펙터링
- uitableview
- clean architecture
- SWIFT
- Clean Code
- 클린 코드
- 리펙토링
- Human interface guide
- combine
- rxswift
- UITextView
- collectionview
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 1. 타임아웃 처리 방법(스레드 락 thread lock, DispatchWorkItem, wait(timeout:)) 본문
iOS 응용 (swift)
[iOS - swift] 1. 타임아웃 처리 방법(스레드 락 thread lock, DispatchWorkItem, wait(timeout:))
jake-kim 2023. 3. 31. 02:311. 타임아웃 처리 방법 - DispatchWorkItem 사용 <
2. 타임아웃 처리 방법 - DispatchGroup 사용 (+ async를 순서대로 처리 방법)
(async 작업이 하나이면 DispatchWorkItem을 사용하고, async 작업이 두 개 이상이면 DispatchGroup 사용)
타임아웃 처리 아이디어
- 시나리오) 아래와 같이 someDelayWork 함수 내부에서 몇초가 걸릴지 모르고 스레드 락을 거는 작업이 있을때 사용하는쪽에서 타임아웃을 주어 일정 시간안에 응답이 안오면 에러를 띄우고 싶은 경우?
let value = someDelayWork()
private func someDelayWork() -> Int {
sleep(10)
return 3
}
- 타임아웃 추가 아이디어
- 현재 메인스레드이므로, 스레드 락이 걸리는 지점에서 메인스레드도 락이 걸릴 위험이 있기 때문에 별도의 큐를 만들어 someDelayWork()를 호출하게끔 수행
- DispatchWorkItem을 사용하면 타임아웃 거는게 매우 쉽기 때문에 DispatchWorkItem 인스턴스를 생성하여 위에서 만든 큐와 같이 사용
사전지식) Queue의 종류
- main queue (serial queue): DispatchQueue.main
DispatchQueue.main
- global queue (concurernt, qos 설정 가능)
// 애니메이션과 같은 UI 즉시 업데이트가 필요하며, 멈춘것처럼 보이지 않는 작업들 (유저의 반응)
DispatchQueue.global(qos: .userInteactive)
// 저장된 문서를 열거나, 유저가 무언가 클릭했을 때 작업을 수행하고 즉각 보여주어야 하는 것 (몇초)
DispatchQueue.global(qos: .userInitiated)
// 기본 서비스 (일반적인 경우 - userInitiated와 utility의 중간정도의 우선순위)
DispatchQueue.global(qos: .default)
// 보통 프로그레스바를 같이 사용하는 작업
DispatchQueue.global(qos: .utility)
// 유저에게 표시되지 않는 동기화, 안정화, 백업과 같은 일
DispatchQueue.global(qos: .background)
- custom queue (디폴트는 serial이며 concurrent로 변경 가능)
let mySerialQueue = DispatchQueue(label: "myQueue")
let myConcurrentQueue = DispatchQueue(label: "myQueue", attributes: .concurrent)
DispatchWorkItem을 이용한 타임아웃 구현
- 필요한 기능
- 정해진 시간안에 안오면 타임아웃 에러 처리
- 정해진 시간안에 오면 타임아웃을 끝내고 성공 처리
- 적용 전
let value = someDelayWork()
private func someDelayWork() -> Int {
sleep(10)
return 3
}
- someDelayWork를 호출하는쪽의 스레드 락이 걸리므로, 메인 스레드가 아닌 별도의 스레드 생성
- global도 전역적으로 사용되는 것이므로 커스텀 큐로 사용
let queue = DispatchQueue(label: "work_queue")
let workItem = DispatchWorkItem { [weak self] in
let value = self?.someDelayWork()
}
- workItem 인스턴스의 wait(timeout:) 메소드를 사용하면 타임아웃을 거는게 쉽고, 리턴타입으로 타임아웃이 일어났는지도 쉽게 파악이 가능
queue.async(execute: workItem)
let result = workItem.wait(timeout: DispatchTime.now() + 10)
switch result {
case .success:
print("success")
case .timedOut:
print("timedOut")
}
(결과)
let queue = DispatchQueue(label: "work_queue")
let workItem = DispatchWorkItem { [weak self] in
let value = self?.someDelayWork()
}
queue.async(execute: workItem)
let result = workItem.wait(timeout: DispatchTime.now() + 10)
switch result {
case .success:
print("success")
case .timedOut:
print("timedOut")
}
private func someDelayWork() -> Int {
sleep(5)
return 3
}
확인) 타임아웃전에 someDelayWork()가 끝난 경우 - success
let queue = DispatchQueue(label: "work_queue")
let workItem = DispatchWorkItem { [weak self] in
print("start")
let value = self?.someDelayWork()
print("end")
}
queue.async(execute: workItem)
print("test>")
let result = workItem.wait(timeout: DispatchTime.now() + 10)
switch result {
case .success:
print("success")
case .timedOut:
print("timedOut")
}
private func someDelayWork() -> Int {
sleep(5)
return 3
}
/*
test>
start
end
success
*/
확인) someDelayWork()가 늦게 끝나서 타임아웃에 도달한 경우 - timedout
- 주의할점: 타임아웃이 걸려도 someDelayWork()가 끝난 시점에 데이터를 받아오기 때문에 (end가 출력) 취소되었을때 작업을 중단하는 별도의 코드도 필요
let queue = DispatchQueue(label: "work_queue")
let workItem = DispatchWorkItem { [weak self] in
print("start")
let value = self?.someDelayWork()
print("end")
}
queue.async(execute: workItem)
print("test>")
let result = workItem.wait(timeout: DispatchTime.now() + 5)
switch result {
case .success:
print("success")
case .timedOut:
print("timedOut")
}
private func someDelayWork() -> Int {
sleep(10)
return 3
}
/*
test>
start
timedOut
end
*/
- workItem.cancel()을 timeout나는 곳에서 호출해도 안 클로저는 다 실행되는것을 주의 (위에서 end 출력)
- cnacel()을 사용하면 아래처럼 isCanclled 프로퍼티가 true로 되어 이것을 사용할때 적용
let result = workItem.wait(timeout: DispatchTime.now() + 1)
switch result {
case .success:
print("success")
case .timedOut:
workItem.cancel()
print("isCancelled?", workItem.isCancelled)
print("timedOut")
}
- timeout 핸들링이 필요한 경우 아래처럼 전역변수에 isCancelled를 두고 사용
class ViewController: UIViewController {
var isCancelled = false
override func viewDidLoad() {
super.viewDidLoad()
isCancelled = false
let queue = DispatchQueue(label: "work_queue")
let workItem = DispatchWorkItem { [weak self] in
print("start")
let value = self?.someDelayWork()
guard self?.isCancelled == false else { return }
print("end", value)
}
queue.async(execute: workItem)
print("test>")
let result = workItem.wait(timeout: DispatchTime.now() + 1)
switch result {
case .success:
print("success")
case .timedOut:
workItem.cancel()
self.isCancelled = workItem.isCancelled
print("timedOut")
}
}
private func someDelayWork() -> Int {
sleep(3)
return 3
}
}
* 전체 코드: https://github.com/JK0369/ExTimeout
* 참고
https://ios-development.tistory.com/1201
'iOS 응용 (swift)' 카테고리의 다른 글
Comments