-
동시성 프로그래밍(8) OperationQueueiOS 2022. 3. 22. 00:46
안녕하세요.
오늘은 OperationQueue에 대해서 알아보도록 하겠습니다.
1. OperationQueue
https://developer.apple.com/documentation/foundation/operationqueue
지난 시간까지 Operation에 대해서 알아보았는데요.
OperationQueue는 이름 그대로 Operation들의 실행을 제어하는 Queue입니다.
DispatchQueue를 생각하면서 OperationQueue는 어떤 속성들을 가지고 있는지 하나씩 확인해보겠습니다.
1) Serial or Concurrent?
OperationQueue는 maxConcurrentOperationCount라는 프로퍼티를 가지고 있습니다.
동시에 진행할 작업의 수를 결정하는 프로퍼티입니다.
OperationQueue를 생성하고 maxConcurrentOperationCount의 기본값을 확인하면 -1인 것을 확인할 수 있습니다.
이 값이 -1일 경우 시스템이 알아서 현재 상태에 따라 동적으로 동시 진행할 작업의 수를 결정합니다.
그렇다면 이 값을 1로 바꾼다면 Serial한 Queue를 만들 수 있겠죠?
class HJOperation: Operation { var number: Int init(number: Int) { self.number = number } override func main() { print("작업\(self.number) 시작") sleep(3) print("작업\(self.number) 완료") } }
위와 같이 Operation 클래스를 만들고
let op1 = HJOperation(number: 1) let op2 = HJOperation(number: 2) let op3 = HJOperation(number: 3) let queue = OperationQueue() queue.addOperation(op1) queue.addOperation(op2) queue.addOperation(op3)
Operation 3개를 생성해 Queue에 추가해보겠습니다.
작업이 동시에 진행되는 것을 확인할 수 있습니다.
만약 maxConcurrentOperationCount를 1로 설정한다면
let op1 = HJOperation(number: 1) let op2 = HJOperation(number: 2) let op3 = HJOperation(number: 3) let queue = OperationQueue() queue.maxConcurrentOperationCount = 1 queue.addOperation(op1) queue.addOperation(op2) queue.addOperation(op3)
작업이 하나씩만 Serial하게 실행됩니다.
이 값을 1이 아닌 값으로 변경해 동시에 진행되기를 원하는 작업의 수를 직접 정할 수 도 있겠죠?
2) Quality of Service
https://developer.apple.com/documentation/foundation/operationqueue/1417919-qualityofservice
OperationQueue도 DisaptchQueue와 마찬가지로 qualityOfService 프로퍼티를 이용하여 QoS를 설정할 수 있습니다.
OperationQueue의 QoS는 DispatchQueue의 6종류에서 .unspecified를 제외한 5개로 이루어져 있습니다.
역시 userInteractive > userInitiated > default > utility > background 순입니다.
직접 생성한 OperationQueue의 경우 기본값은 background입니다.
OperationQueue 또한 main 메서드를 통해 main Queue를 제공하는데요. 메인 큐에 경우에는 userInteractive가 기본값입니다.
let userInteractiveQueue = OperationQueue() let backgroundQueue = OperationQueue() userInteractiveQueue.qualityOfService = .userInteractive backgroundQueue.qualityOfService = .background backgroundQueue.addOperation(op1) userInteractiveQueue.addOperation(op2)
QoS가 다른 두 개의 Queue를 생성하고 작업을 추가하면
QoS가 높은 Queue의 작업이 먼저 실행되는 것을 확인할 수 있습니다.
operationQueue뿐만이 아니라 각각의 Operation에도 QoS를 설정할 수 있습니다.
만약 Operation의 QoS가 명시되어 있을 경우 해당 QoS를 따릅니다.
Operation의 QoS는 기본값이 default입니다.
op1.qualityOfService = .userInitiated // op2의 QoS를 userInitiated로 증가 let defaultQueue = OperationQueue() let backgroundQueue = OperationQueue() defaultQueue.qualityOfService = .default backgroundQueue.qualityOfService = .background backgroundQueue.addOperation(op1) defaultQueue.addOperation(op2)
op1의 QoS를 userInitiated로 증가시키면
backgroundQueue에 추가되었음에도 불구하고 op1이 먼저 실행되는 것을 확인할 수 있습니다.
또한 OperationQueue는 작업을 호출할 DispatchQueue를 지정할 수 underlyingQueue 프로퍼티가 있습니다.
만약 underlyingQueue가 명시된다면 최우선적으로 해당 Queue의 QoS를 적용합니다.
op1.qualityOfService = .userInitiated // op1의 QoS를 userInitiated를 증가 let defaultQueue = OperationQueue() let backgroundQueue = OperationQueue() defaultQueue.underlyingQueue = DispatchQueue.global(qos: .userInteractive) // global userInteractiveQueue를 defaultQueue의 underlyingQueue로 지정 defaultQueue.qualityOfService = .default backgroundQueue.qualityOfService = .background backgroundQueue.addOperation(op1) defaultQueue.addOperation(op2)
위와 같이 default QoS를 가지고 있던 Queue의 underlyingQueue를 userInteractiveQueue로 설정하면
다시 op2가 먼저 시작되는 것을 확인할 수 있습니다.
따라서 underlyingQueue의 QoS > Operation의 QoS > OperationQueue의 QoS 순으로 QoS가 정해진다고 볼 수 있습니다.
3) Operation 추가 방법
총 3가지 방법으로 Operation을 추가할 수 있습니다.
- func addOperation(Operation)
앞서 했던 방식대로 Opertaion을 생성해 추가하는 방식입니다.
- func addOperation([Operation], waitUntilFinished: Bool)
Operation들의 배열을 추가하는 방식입니다.
waitUntilFinished를 true로 설정했을 시 Operation들이 모두 완료될 때까지 현재 스레드를 차단합니다.
당연히 main 스레드에서의 사용은 주의해야겠습니다.
- func addOperation(() -> Void)
DispatchQueue와 마찬가지로 클로저를 그대로 추가하는 것도 가능합니다.
Operation이 OpertaionQueue에 추가되면
start() 메서드를 호출할 필요 없이 스레드에 배정되면 isExecuting 프로퍼티가 true가 됩니다.
Opertaion의 isFinished 프로퍼티가 true가 되면(완료되거나, 취소되거나)
OpertaionQueue를 떠납니다.
4) waitUntilAllOperationsAreFinished()
OpertaionQueue의 존재하는 Operation이 모두 완료될 때까지 현재 스레드를 차단하는 메서드입니다.
단 현재 스레드에서는 OperationQueue에 작업 추가가 당연히 불가능하지만 다른 스레드에서는 추가가 가능합니다.
추가한 작업들도 모두 완료되어야 차단을 해제합니다.
backgroundQueue.addOperation(op1) backgroundQueue.addOperation(op2) backgroundQueue.waitUntilAllOperationsAreFinished() print("complete") backgroundQueue.addOperation(op3) //op3는 나중에 추가된다.
addOpertaion(op3)의 경우는 op1, op2가 마무리되어야 실행됩니다.
하지만 waitUntilAllOperationsAreFinished()를 호출한 스레드와 다른 스레드에서 작업을 추가한다면
backgroundQueue.addOperation(op1) backgroundQueue.addOperation(op2) DispatchQueue.global().async { backgroundQueue.waitUntilAllOperationsAreFinished() print("complete") } backgroundQueue.addOperation(op3)
새로 추가된 작업까지 완료되어야 현재 스레드 차단을 해제합니다.
5) 일시중지 - 재개
https://developer.apple.com/documentation/foundation/operationqueue/1415909-issuspended
OperationQueue는 isSuspended 프로퍼티를 가지고 있습니다.
이 값을 true로 변경한다면 현재 실행 중인 작업은 일단 계속 실행하지만 새로운 작업을 실행하지는 않습니다.
이 상태에서도 Queue에 작업 추가는 가능합니다.
이 속성을 false로 변경한다면 작업 재개가 가능합니다. 기본값은 false입니다.
오늘은 OperationQueue에 대해서 알아보았습니다.
알아본 속성 이외에도 다양한 속성이 있으니 더 공부하면서 알아보도록 하겠습니다.
Ref.
https://www.inflearn.com/course/iOS-Concurrency-GCD-Operation/dashboard
'iOS' 카테고리의 다른 글
Accessibility - VoiceOver (0) 2022.04.12 SwiftUI - Stack (0) 2022.03.24 UIResonder & Responder Chain (0) 2022.03.17 동시성 프로그래밍(7) Operation (0) 2022.03.15 동시성 프로그래밍(6) Race Condition(경쟁 상태) 해결 방법 (1) 2022.03.09