Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

iOS 개발일지

iOS 비동기/동시성 프로그래밍에 대해 알아보자! 본문

iOS

iOS 비동기/동시성 프로그래밍에 대해 알아보자!

Lia's iOS 2023. 2. 3. 02:31

 

개발을 하다보면 필연적으로 비동기/동시성 프로그래밍의 필요성을 느끼게 된다.

개인적으로는 배너 스크롤 뷰를 만들었을 때 가장 와닿았었다.

그냥 조금 빨라졌당! 정도가 아닌, 정말 절실할 정도의 속도 차이가 있었다.

 

 

GCD와 DispatchQueue

 

그럼 이러한 비동기/동시성 프래그래밍은 어떻게 하느냐!

선입선출을 하는 Queue에 Task를 저장하면, GCD가 스레드를 적절히 생성해 Queue에 담긴 Task를 분배해준다.

여기서 GCD에서 관리하는 Queue의 이름이 DispatchQueue이다.

 

음...! 너무 어려우니 패스트푸드점으로 예시를 들어보자!

44회차 정리 - GCD 를 읽고 오면 이해가 더 편할 것이다.

 

 

 

 

비동기 프로그래밍

 

비동기 프로그래밍은 여러 일을 동시에 처리해야 할 때 매우 효율적이다.

만약 동기 방식이라면 1번 손님이 주문한 햄버거를 모두 만들기 전까지는 2번 손님의 주문 내용은 확인조차 안 할 것이다.

대충 예시들었는데 왠지 좀 킹받네....

엄청 비효율적이다 싶지만, 이렇게 일을 주문이 들어온 순서대로 하는 것이 프로그램의 기본 동작 방식이다.

 

이번에는 그 효율적이라는 비동기 방식으로 한번 해보자.

어느 날 주문이 너무 몰려서 고민인 메인 알바생 (스레드) 이 매니저님 (GCD) 에게 부탁했다고 가정하는 것이다.

 

"1번부터 5번 햄버거 주문 넘겨드릴테니까 재분배해주세요. 저는 6번부터 10번까지 만들고 있을게요!"

 

이후 메인 알바생이 가벼운 마음으로 10번 햄버거까지 다 만들었더니, 매니저에게 아까 넘긴 일이 다시 자신에게 돌아와 있는 것이 아닌가...

이것이 Dispatchqueue.main.async 이다.

매니저님에게 넘긴 일은 신경쓰지 않고 (앞 작업이 끝나는 것을 기다리지 않고) 다음 일을 시작하긴 하지만, 알바생은 메인 알바생 혼자이기 때문에 결국 본인에게 재분배된다.

 

 

 

 

동시성 프로그래밍

 

이제 Dispatchqueue.global().async, 즉 동시성은 대충 예상이 간다.

비동기는 햄버거 주문을 재분배한다고 했지만, 알바생이 결국 한명뿐이었다.

알바생이 여러명이라면 당연히 속도도 더 빨라지고, 기존에 혼자 열심히 햄버거를 만들던 메인 알바생의 노고를 덜어줄 수 있을 것이다.

위와 마찬가지로 메인 알바생은 매니저에게 햄버거 주문을 넘기고, 넘긴 주문은 신경쓰지 않고 자신에게 남아있는 일만 수행한다.

이때 동시성 방식이라면, 매니저는 다른 알바생을 고용해 넘겨받은 주문을 적절히 재분배하고 햄버거를 여러명이서 동시에 만들 것이다.

 

 

 

 

QoS (Quality of Service)

 

GCD에서 global 큐를 사용할 때, qos를 사용해 중요도에 따라 작업을 분류하여 우선순위를 지정할 수 있다.

한정된 에너지를 효율적으로 사용하기 위해서는 빠른 수행이 필요한 작업과 그렇지 않은 작업을 나누어 처리하는 것이 중요하다.

 

qos 6가지 종류가 있으며

userInteractive, userInitiated, default, utility, background, unspecified 순으로 중요도가 높다.

DispatchQueue.global(qos: .userInteractive)
사용자와 직접적인 상호작용을 하는 UI 관련 이벤트
소요시간 : 거의 즉각적

DispatchQueue.global(qos: .userInitiated)
파일을 열거나, 사용자가 UI를 통해 발생시킨 액션 처리 등
소요시간 : 몇 초 이하와 같이 거의 즉각적

DispatchQueue.global()
일반적인 작업 (default)

DispatchQueue.global(qos: .utility)
데이터 다운로드 또는 가져오기와 같은 작업에 사용하며 응답성, 성능 및 에너지 효율성간의 균형을 제공
유틸리티 작업에는 일반적으로 사용자에게 표시되는 진행률 표시줄이 있음
소요시간 : 몇 초에서 몇 분

DispatchQueue.global(qos: .background)
유저에게 직접적으로 표시되지 않는, 백그라운드에서 처리되는 작업
인덱싱, 동기화 및 백업과 같은 작업에 사용하며 속도보다는 에너지 효율성 중시
소요시간 : 몇 분 또는 몇 시간

DispatchQueue.global(qos: .unspecified)
사용하지 않음

 

이렇게 GCD와 QoS만으로도 비동기 프로그래밍을 통한 성능 향상이 가능하다.

하지만 GCD는 종속성 (A 다음에 꼭 B가 실행되게끔) 을 설정하는 것이 어렵다는 단점이 있다.

위의 예시와 같이 비교적 간단한 작업이라면 전혀 문제되지 않지만, 조금 더 복잡하고 섬세한 컨트롤이 필요한 작업이라면 Operation Queue가 더 적합할 수 있다. 

 

 

 

 

Operation Queue

 

Operation Queue는 GCD와 달리 task의 실행, 정지, 대기와 같은 실행 상태를 알 수 있고,

이를 통해 Operation들을 취소하거나 순서를 지정할 수 있다.

진행 상황과 종속성을 추적하면서 여러 클래스에 대한 책임을 분리할 수 있게 된다.

 

Operation이란, single task에 관한 데이터와 코드를 나타내는 추상 클래스이다.

해당 클래스를 서브클래싱하여 사용하면 안정적으로 task를 실행시킬 수 있다.

Operation을 서브클래싱 했다는 것은, 기능을 캡슐화 했다는 의미임으로 기능에 관한 모듈성이 향상된다.

 

Operation Queue란, Operation 객체들을 프로퍼티에 의해 실행시키는 Queue이다.

한번 Operation Queue에 Operation 객체를 넣으면, task가 끝날 때까지 Queue가 없어지지 않고 존재하게 된다.

해당 Queue에서 Operation들을 삭제하는 등의 task 관리가 가능하다.

 

참고 : Operation, Operation Queue 둘다 내부적으로 GCD를 사용한다.

 

 

 

 

Operation State

 

Operation에는 내부적으로 상태값들이 존재하기 때문에 더 세세한 처리가 가능하다.

 

 

  • Pending : Queue에 Operation이 추가 될 때
  • Ready : Pending에서 모든 조건이 충족되면 해당 상태로 변경
  • Executing : start() 메소드를 호출하여 작업이 시작된 경우
  • Finished : 작업이 완료된 경우 해당 상태로 변경되고 Queue에서 Operation이 제거
  • Cancelled : 해당 상태는 3개의 상태 Pending, Ready, Executing에서만 변경 가능하고, Cancelled 상태가 되었다가 곧바로 Finished 상태로 변경