TIL(Today I Learned)

4월 8일(목)

학습내용

  • Concurrency 프로그래밍 with 앨런
    • 동시성 프로그래밍이 필요한 이유
      • 물리적인 Thread vs 소프트웨어적인 Thread
      • 메인쓰레드에서 Main Run Loop와 Update Cycle이 1초에 60번 계속 실행되고 있다.
      • 그래서 메인쓰레드에서 네트워킹작업을 하면 뷰의 업데이트에 버벅임이 발생하게 된다.
      • 이를 동시성 프로그래밍으로 작업을 분산시켜서 버벅임을 해결할 수 있다.
      • iOS 에서는 분산작업을 해주는 큐를 제공한다. 이 큐에 작업을 넣으면 자동으로 쓰레드에 분산해서 실행하게 된다.
    • DispatchQueue vs OperationQueue
      • DispatchQueue는 간단한 작업 (서버통신, 데이터 및 이미지 다운로드 등)을 할때 사용한다.
      • OperationQueue는 내부가 GCD로 구현되어있으며, 작업을 객체화한다. 작업 취소기능, 순서지정, 일시중지/ 상태추적 같은 추가기능을 제공한다.
      • 물리적인 쓰레드에서 동시에 일하는것을 병렬(Parallel), 소프트웨어적 쓰레드 객체에서 동시에 일을 하는것은 동시성(Concurreny)이다.
      • OS 영역의 Thread pool에 NSThread로 구현된 소프트웨어 쓰레드가 있고, 앱 단위의 소프트웨어 쓰레드가 있다. Concurrency 큐는 이 앱단위의 쓰레드에 작업을 할당하고, 앱 단위의 쓰레드를 OS영역의 Thread에 작업을 분산시키며, OS영역의 Thread는 최종적으로 물리적인 Thread에 분산되어 실행된다.
      • Sync는 작업을 다른쓰레드에 보내고 기다리는것이고, async는 기다리지 않고 다른일을 실행하는 것이다.
      • Swift는 Sync는 Blocking 개념으로, Async는 Non-Blocking의 개념으로만 사용한다.
      • Serial은 한개의 큐에 순서대로 작업을 보내는것이고, Concurrenct은 여러 쓰레드에 분산해서 작업을 보내는 것이다.
      • Serial 큐는 순서가 중요할때 사용하고, Concurrent 큐는 각자 독립적이지만 유사한 여러개의 작업을 처리할때 사용한다.
      • GCD는 main, global, private(custom)가 있고, Operation Queue는 main, private(custom)가 있다.
      • DispatchQueue의 qos에 따라 작업을 배치할 쓰레드의 개수의 차이가 생기며, 이에 따라 소요시간의 차이가 생긴다. (보통 qos는 default나 utility로 많이 사용한다.)
      • DispatchQueue는 여러종류의 큐가 존재하며, 일반적인 겨우 DispatchQueue.global()을 사용한다. (Concurrency 큐이다.)
      • DispatchQueue는 Custom 큐를 만들 수 있는데, Custom 큐는 기본적으로 serial 큐이지만 Concurrency 큐로도 설정이 가능하다.
    • Concurrency 프로그래밍 주의사항
      • UI 관련된 일들은 메인쓰레드에서 실행해야한다. iOS뿐 만 아니라 많은 OS가 메인쓰레드 이외에서 UI 업데이트를 하지 못하게 설계가 되어있는데, 여러 쓰레드에서 UI를 업데이트를 할 경우 뷰의 동작이 이상하게 작동할 수 있다는 점과 성능상의 이유 때문이다.
      • 비동기 함수는 리턴형으로 만들면 안되고(언제 비동기 작업이 끝날지 모르니깐), CompletionHandler를 받아서 이것으로 비동기 작업이 끝난 결과를 다루게 해야한다.
    • Dispatch Group
      • 작업을 그룹짓고, 그룹내의 작업이 모두 끝나는 시점을 알고 싶을때 사용한다. 예) 여러 애니메이션 효과가 겹쳐있을때, 애니메이션이 모두 종료된 시점을 알고싶다.
      • 그룹화된 작업내에 비동기 작업이 있으면 제대로된 종료시점을 알 수 없으므로, enter와 leave를 사용한다. enter를 사용하여 비동기 작업이 시작함을 알리고, 비동기 작업 내에서 leave를 호출하여 비동기 작업이 끝났다는 것을 알린다. 이로써 제대로된 종료시점을 알 수 있다.
    • OperationQueue
      • 작업을 Operation 객체로 만들고 OperationQueue에 넣어서 실행할 수 있다. (DispatchQueue처럼 블록으로 만들어서 실행도 가능)
      • Operation은 작업을 클래스화한 것이다. (작업을 추상화)
      • Operation을 상속하고, input 변수 정의, output 변수 정의, main()을 재정의를 하면 된다.
      • Operation 생명주기는 isReady, isExecuting, isCancelled, isFinished가 있다.
      • 비동기 Operation은 상태관리를 통해 완료 시점을 제대로 다룰 수 있다. 즉, main 내의 비동기 함수가 종료될때 컴플리션 핸들러에서 상태를 완료로 바꿔주는 코드를 작성하면 된다. (AsyncOperation이라는 코드를 구글링해서 가져와서 사용하면 된다.)
      • Operation 의 cancel() 메서드를 호출하면 Operation의 isCancelled 가 true가 되는 것이고 실제로 작업이 취소가 되는건 아니다. 따라서 작업 내부에 isCancelled 가 true면 return 하도록 코드를 짜서 작업을 취소시킬 수 있다.
      • Operation에 의존성을 설정해서 작업 순서를 지정할 수 있다. 그리고 순서가 설정된 작업들 사이의 데이터 전달은 데이터 전달 프로토콜을 만들어서 할 수 있다.

문제점/고민한점

  • Blocking, Non-Blocking 의 차이점은?

해결방법

  • 호출된 함수가 바로 리턴해서 호출한 함수에게 제어권을 넘겨주고, 호출한 함수가 다른 일을 할 수 있는 기회를 줄 수 있으면 Non-Blocking이다. 그렇지 않고 호출된 함수가 자신의 작업을 모두 마칠 때까지 호출한 함수에게 제어권을 넘겨주지 않고 대기하게 만든다면 Blocking이다.

참고

https://homoefficio.github.io/2017/02/19/Blocking-NonBlocking-Synchronous-Asynchronous/