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/