Swift OperationQueue 사용해 보기

어떤 운영체제라도 스레드 관련 사항은 매우 중요하다. iOS 에서는 직접 스레드를 생성하기 보다는 큐에 작업을 넣으면 임의의 스레드들이 작업하게 되는 방식이 주로 사용된다. 메인큐에 넣으면 메인스레드에서만 작업이 된다. 큐에 넣어진 작업은 시스템이 제공하는 임의의 스레드에서 실행 되고 특정 스레드에서만 실행되도록 하려면 스레드를 생성해야 한다.

iOS 에서 제공하는 OperationQueue 와 DispatchQueue 는 비슷하지만 OperationQueue에서 좀 더 세밀한 처리가 가능하다. 다음은 OperationQueue, DispatchQueue 사용 예시이다.

//
let dq = DispatchQueue(label: "dq")
dq.async {
    print("working with dispatch queue")
}
DispatchQueue.global().async {
    print("working with dispatch queue")
}

// 
let oq = OperationQueue()
oq.addOperation {
    print("working with operation queue")
}

OperationQueue 보다 사용이 편리하고 이해하기 쉬운 DispatchQueue 가 많이 사용되지만 OperationQueue를 사용해야 하는 경우가 있다.

1. 큐에서 동시에 처리되는 작업의 갯수를 정해야 할 때
2. 큐에 넣어진 작업을 중지, 취소, 재개해야 할 때 (DispatchWorkItem 취소는 가능함)

내가 작업해 본 경험에 의해 그리 흔하지는 않지만 위 2가지를 정해야 하는 경우가 있었다.
예를 들어서 빠르게 스크롤 되는 화면에 표시되는 데이터를 앱 디비로 부터 읽어와서 표시되는 경우 이미 스크롤 넘어간 구간의 작업은 취소할 필요가 있다.

다음은 약간의 구체적인 OperationQueue 사용 예이다.
큐에서 동시에 처리되는 작업의 갯수를 1로 했고 (디폴트는 -1값으로 동시에 처리되는 작업의 갯수 제한이 없다) 임의의 작업을 취소 처리하였다.

let queue = OperationQueue()
work()

class CustomOperation: Operation {
    var index: Int = 0
    
    init(index: Int) {
        self.index = index
    }
    
    override func main() {
        print("working, index \(index) thread \(Thread.current)")
    }
}

func work() {
    // 동시 실행할 수 있는 작업수를 1개로 지정
    queue.maxConcurrentOperationCount = 1

    // 10개의 Operation을 추가함
    for i in 0 ..< 10 {
        let op = CustomOperation(index: i)
        op.completionBlock = { [weak op] in
            guard let op = op else { return }
            print("complete, index \(op.index) isCancelled \(op.isCancelled)")
        }
        queue.addOperation(op)
    }
    
    cancel()
}

func cancel() {
    // 랜덤 선택된 인덱스의 작업을 취소 가능하면 취소함
    let r = Int.random(in: 0 ..< 10)
    for case let o as CustomOperation in queue.operations where !o.isCancelled && o.index == r {
        queue.isSuspended = true
        defer {
            self.queue.isSuspended = false
        }

        o.cancel()
        print("#### operation cancelled index \(r) ####")
    }
}
// 실행 결과, 랜덤으로 선택된 8번 인덱스의 작업이 취소된 것을 볼 수 있다.

working, index 0 thread <NSThread: 0x60000371c940>{number = 5, name = (null)}
#### operation cancelled index 8 ####
working, index 1 thread <NSThread: 0x60000371c940>{number = 5, name = (null)}
complete, index 0 isCancelled false
complete, index 1 isCancelled false
working, index 2 thread <NSThread: 0x60000371c940>{number = 5, name = (null)}
complete, index 2 isCancelled false
working, index 3 thread <NSThread: 0x60000371c180>{number = 4, name = (null)}
complete, index 3 isCancelled false
working, index 4 thread <NSThread: 0x60000371c180>{number = 4, name = (null)}
complete, index 4 isCancelled false
working, index 5 thread <NSThread: 0x60000371c940>{number = 5, name = (null)}
complete, index 5 isCancelled false
working, index 6 thread <NSThread: 0x60000371c180>{number = 4, name = (null)}
complete, index 6 isCancelled false
working, index 7 thread <NSThread: 0x60000371c940>{number = 5, name = (null)}
complete, index 7 isCancelled false
complete, index 8 isCancelled true
working, index 9 thread <NSThread: 0x60000371c180>{number = 4, name = (null)}
complete, index 9 isCancelled false

답글 남기기

이메일 주소는 공개되지 않습니다.