ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 동시성 프로그래밍(7) Operation
    iOS 2022. 3. 15. 20:49

    안녕하세요.

    지난 시간까지는 GCD를 이용한 동시성 프로그래밍에 대해서 배웠습니다.

     

    이제부터는 동시성 프로그래밍을 위한 또 다른 방법, Operation에 대해서 알아보겠습니다.


    1. GCD와의 차이

     

    GCD를 이용해 동시성 프로그래밍을 할 수 있는데 Operation은 왜 필요할까요?

     

    Operation도 내부적으로는 GCD를 이용해 동작합니다.

    하지만 Operation은 GCD가 할 수 없는 조금 더 복잡한 일을 할 수 있습니다.

     

    우선 Operation은 데이터와 기능을 캡슐화한 클래스입니다.

    따라서 작업의 재사용성이 향상됩니다.

    이미지 다운로드와 같이 반복되는 작업의 경우 Operation으로 정의해놓으면 Operation을 인스턴스화 하는 것만으로 작업을 생성할 수 있는 것이죠.

     

    또한 Operation의 경우에는 작업의 취소, 순서지정, 상태 추적이 가능합니다.

    DispatchWorkItem의 경우에는 약하게 지원하던 기능들을 Operation으로 더 강력하게 지원합니다.

     

    그럼 Operation부터 알아보도록 하겠습니다.


    2. Operation

    https://developer.apple.com/documentation/foundation/operation

     

    Apple Developer Documentation

     

    developer.apple.com

    Operation은 하나의 작업에 대하여 관련된 코드와 데이터들을 나타내는 추상 클래스입니다.

    추상 클래스이기 때문에 직접 사용하지 않고 Operation을 상속받은 클래스를 생성하는 것으로 원하는 작업은 만들 수 있습니다.

     

    Operation은 Single-Shot 객체입니다.

    Single-Shot 객체는 한 번만 사용이 가능한 객체를 의미합니다.

    Operation의 경우에는 작업이 완료된 인스턴스를 다시 실행시킬 수 없습니다.

     

    Operation이 제공하는 메서드를 알아보겠습니다.

    - start()

    이름 그대로 작업을 시작하는 메서드입니다.

    이 메서드를 호출하게 되면 작업 상태를 업데이트하고 main() 메서드를 호출합니다.

     

    위의 과정은 동기적으로 실행됩니다. 

    따라서 일반적으로는 이 메소드를 재정의할 필요는 없습니다.

    하지만 작업이 동시적으로 실행되기를 원하는 경우에는 start() 메서드를 재정의해야 한다고 합니다.

    start() 메서드를 재정의한 경우 super 키워드를 호출해서는 안됩니다.

    isExecuting, isFinished, isAsynchronous 프로퍼티를 작업의 상태에 따라 변경하여야 합니다.

     

     +

    OperationQueue를 이용하여 작업을 실행하게 될 경우, 기본적으로 비동기적으로 동작합니다.

    start()메소드를 재정의해야 하는 경우가 헷갈려서 찾아본 결과 OperationQueue에 의해서 관리되는 작업이 아닌 작업 스스로 비동기적으로 실행되기를 원할 경우 재정의한다고 합니다.

    https://stackoverflow.com/questions/38546198/nsoperation-start-vs-main

     

    NSOperation, start vs main

    According to Apple document on NSOperation, we have to override main method for non-concurrent operations and start method for concurrent operations. But why?

    stackoverflow.com

     

    - main()

    Operation이 수행하기를 원하는 작업을 main() 메서드를 오버라이딩하여 작성하면 됩니다.

    기본적으로 아무런 일도 하지 않습니다. 따라서 super 키워드를 호출할 필요가 없습니다.

    - cancel()

    cancel() 메서드는 직접적으로 작업을 멈추는 메서드는 아닙니다.

    cancel() 메서드는 isCancelled 프로퍼티를 true로 변경합니다.

     

    만약 작업이 실행되지 않은 경우라면 작업을 실행시키지 않습니다.

    진행 중인 작업을 중단하고 싶은 경우에는 재정의한 main() 메서드 또는 start() 메서드에 isCancelled 프로퍼티를 확인하는 구문을 넣어야 합니다.


    또한 Operation은 작업의 상태 추적이 가능하기 때문에 상태를 나타내는 프로퍼티들을 가지고 있습니다.

     

    - isReady
    - isExecuting 
    - isCancelled
    - isFinished

     

    이름만 들어도 작업이 어떤 상태인지 알 수 있을 것 같습니다.

     

    직접 확인해보겠습니다.

     

    class HJOperation: Operation {
        
        override func main() {
            sleep(3)
            print("main", "isReady:",isReady, ",isExecuting:", isExecuting,",isCancelled:", isCancelled,",isFinished:", isFinished)
        }
    }

    main() 메서드에 상태를 나타내는 프로퍼티들을 출력하는 코드를 작성했습니다.

     

    1. 생성 후 start()

    let op = HJOperation()
    print("생성 후", "isReady:",op.isReady, ",isExecuting:", op.isExecuting,",isCancelled:", op.isCancelled,",isFinished:", op.isFinished)
    op.start()
    print("after start()", "isReady:",op.isReady, ",isExecuting:", op.isExecuting,",isCancelled:", op.isCancelled,",isFinished:", op.isFinished)

     

    인스턴스를 생성하고 start() 메서드를 통해 작업을 실행해보겠습니다.

    인스턴스가 생성되면 isReady 프로퍼티만 true인 것을 확인할 수 있습니다.

    start() 메서드를 호출하면 isExecuting 프로퍼티가 true가 되고 main()메서드를 호출합니다.

    작업이 완료되면 isExecuting 프로퍼티는 false가 되고 isFinished 프로퍼티가 true가 됩니다.

     

    2. 생성 후 start() 호출하지 않고 cancel()

    let op2 = HJOperation()
    op2.cancel()
    print("after cancel()", "isReady:",op2.isReady, ",isExecuting:", op2.isExecuting,",isCancelled:", op2.isCancelled,",isFinished:", op2.isFinished)

    인스턴스를 생성하고 작업을 실행하지 않고 바로 cancel() 메소드를 호출해보겠습니다.

    isCancelled 프로퍼티가 true로 변한 것을 확인할 수 있습니다.

     

    이 상태에서 다시 start() 메소드를 호출하여도 작업을 실행되지 않습니다.

     

    3. start() 호출 후 진행 중인 작업 cancel()

    let op3 = HJOperation()
    op3.completionBlock = {
        print("start() -> cancel()", "isReady:",op3.isReady, ",isExecuting:", op3.isExecuting,",isCancelled:", op3.isCancelled,",isFinished:", op3.isFinished)
    }
    
    let queue = OperationQueue()
    queue.addOperation(op3)
    
    sleep(1) // 작업에 실행을 보장하기 위해서 1초 대기
    op3.cancel()

    실행 중인 Operation에 대해서 cancel() 메서드를 호출시켜보았습니다.

    Operation의 경우 completionBlock 프로퍼티를 통하여 작업 간의 종속성을 부여할 수 있습니다.

     

    또한 start() 메서드의 경우 추가적인 구현이 없을 경우 동기로 실행되기 때문에 실험을 위하여 OperationQueue를 사용하였습니다.

    OperationQueue의 경우 기본적으로는 비동기로 동작합니다.

    실행 중인 작업에 경우 중간에 멈추지는 않습니다. 그러나 isCancelled 프로퍼티가 true가 된 것을 확인할 수 있습니다.

    cancel 된 작업이 완료된 경우 isCancelled 프로퍼티와 isFinished 프로퍼티가 모두 true입니다.

     

    이 과정을 그림으로 표현하면 다음과 같습니다.


    오늘은 Operation에 대해서 알아보았습니다.

    다음 시간에는 Operation들을 관리할 수 있는 OperationQueue에 대해서 알아보겠습니다.


    Ref.

    https://www.inflearn.com/course/iOS-Concurrency-GCD-Operation/dashboard

     

    iOS Concurrency(동시성) 프로그래밍, 동기 비동기 처리 그리고 GCD/Operation - 디스패치큐와 오퍼레이션

    동시성(Concurrency)프로그래밍 - iOS프로그래밍에서 필요한 동기, 비동기의 개념 및 그를 확장한 GCD 및 Operation에 관한 모든 내용을 다룹니다., - 강의 소개 | 인프런...

    www.inflearn.com

     

Designed by Tistory.