멀티 스레드
스레드란?
시스템 레벨에서 프로그램은 나란히(side by side) 실행되며 시스템은 각 프로그램의 필요성에 따라 프로그램에 실행 시간을 할당한다. 프로그램 내부에는 여러 작업을 동시에(또는 거의 동시에) 수행하는 데 사용할 수 있는 하나 이상의 실행 스레드가 있는데, 시스템은 이러한 실행 스레드를 관리하고 사용 가능한 코어에서 실행되도록 스케쥴링 한다.
기술적인 관점에서 스레드는 코드 실행을 관리하는데 필요한 커널 수준(kernel-level) 및 응용 프로그램 수준(application-level) 데이터 구조의 조합이다. 커널 수준의 구조는 스레드에 이벤트를 디스패치하거나 사용 가능한 코어 중 하나에 스레드의 스케줄링을 조정한다. 응용 프로그램 수준의 구조는 함수 호출을 저장하기 위한 호출 스택과 응용 프로그램이 스레드의 속성 및 상태를 관리하고 조작하는데 필요한 구조가 포함된다.
비동시(non-concurrent) 응용 프로그램에는 실행 스레드가 하나만 존재한다. 따라서 이 스레드는 응용 프로그램의 메인 루틴으로 시작하고 끝나며 응용 프로그램의 전체 동작을 구현하기 위해 다른 메서드나 함수로 하나씩 분기된다. 반대로 동시성을 지원하는 애플리케이션은 하나의 스레드로 시작하여 추가적인 실행 경로를 생성하기 위해 필요에 따라 스레드를 추가한다. 각 새 경로에는 응용 프로그램의 메인 루틴에 있는 코드와 독립적으로 실행되는 고유한 사용자 정의 시작 루틴이 있다. 멀티 스레드는 다음과 같은 두 가지 중요한 이점이 있다.
- 멀티 스레드는 응용 프로그램의 응답성(perceived responsiveness)을 향상 시킬 수 있다.
애플리케이션에 스레드가 하나만있는 경우 해당 스레드가 모든 작업을 수행해야 하는데, 문제는 스레드가 한 번에 하나의 작업만 수행 할 수 있다는 것이다. 만약 특정 작업이 완료되는데 오랜 시간이 걸리게 된다면 앱이 사용자 이벤트에 대한 응답을 하지 못하게 되고, 이러한 상황이 오래 지속되면 사용자는 앱이 중단 되었다고 생각하고 강제로 종료할 수 있다.
- 멀티 스레드는 멀티 코어 시스템에서 응용 프로그램의 실시간 성능을 향상시킬 수 있다.
서로 다른 작업을 수행하는 스레드는 서로 다른 프로세서 코어에서 동시에 수행할 수 있으므로 응용 프로그램이 주어진 시간 동안 수행하는 작업의 양을 늘릴 수 있다.
물론 스레드가 응용 프로그램의 성능 문제를 해결하기 위한 만병 통치약은 아니다. 스레드가 제공하는 이점과 함께 잠재적인 문제가 발생하기도 한다. 먼저, 앱에서 여러 실행 경로를 사용하면 코드가 상당히 복잡해질 수 있다. 각 스레드는 앱의 상태 정보가 충돌되지 않도록 다른 스레드와 작업을 조정해야한다. 또한, 단일 앱의 스레드는 동일한 메모리 공간을 공유하기 때문에 같은 데이터 구조에 액세스 할 수 있다. 두 스레드가 같은 데이터 구조를 동시에 조작하려고 하면 한 스레드가 다른 스레드의 변경 사항을 덮어 쓰고 결과 데이터 구조가 손상될 수 있다.
스레딩 용어
이 문서에서는 다음과 같은 용어를 사용한다:
- 스레드(thread)는 별도의 코드 실행경로를 뜻한다.
- 프로세스(process)는 실행 가능한 실행 파일을 나타내며 여러개의 실행 파일을 포함할 수 있다.
- 작업(task)는 수행되어야 할 작업의 추상적인 개념이다.
iOS에서의 멀티 스레드 구현
스레드를 직접 생성하고 관리하는 것은 매우 복잡하고 어려운 일이다. 이러한 작업을 완전하게 이해하지 못한다면 여러 문제에 직면할 수 있다. 따라서 보통 GCD, Operation 같은 스레드의 대체 기술을 사용한다.
-
GCD
Grand Ceentral Dispatch는 스레드 관리보다 작업에 집중하도록 돕는다. GCD를 사용하면 수행하고자 하는 작업을 정의하여 작업 대기열(Work Queue)에 추가할 수 있다. 작업 대기열은 적절한 스레드에 작업을 예약하는 일을 처리한다. 작업 대기열은 사용 가능한 코어 수와 현재 로드에 따라 스레드를 사용하여 작업을 보다 효율적으로 실행한다.
-
Operation
Operation 객체는 부차적인 스레드에서 실행될 작업의 래퍼(Wrapper)이다. 이 래퍼는 작업 수행중에서 스레드 관리 부분을 숨기고 작업 자체에 집중할 수 있도록 돕는다. 이 객체는 일반적으로 operation queue 객체와 결합되어 같이 사용되며 operation queue는 하나 이상의 스레드에서 operation 객체의 실행을 관리하는 역할을 한다.
GCD vs NSOperation
NSOperation
과 GCD **모두 작업 단위를 캡슐화해서 실행을 위해 디스패치 할 수 있게 설계되어 있으며, UI Main 스레드와 분리되어 여러 스레드에서 무거운 작업을 수행하도록 해준다.
GCD - Low-level API
Dispatch queue에 들어온 작업을 serially, concurrently 방식으로 수행할 수 있다. 이 작업들은 시스템에의해 관리되는 스레드 풀에서 수행된다. 작업 아이템을 동기, 비동기 방식으로 지정할 수 있으며, 단순한 구현에 적합하다.
NSOperation - Objective-C 기반 추상 클래스
NSOperationQueue
는 GCD와 비교할때 다음과 같은 장점이 있다.
- dependecy
작업들 사이의 의존성을 추가할 수 있다. 이를 통해 작업의 수행 순서를 지정해줄 수 있다. 많은 병렬 프로세싱이 필요한 작업(수학 방적식)을 위해 사용된다. 병렬 작업들은 서로 의존되는 경우가 많기 때문이다. 예를 들어, 어떤 코드 블럭에 의존하는 작은 코드 블럭이 있다면 이것들을 다 실행하고 필요한 블록이 종료되길 기다리는 것이다.
- Pause, Cancel, Resume
GCD Dispatch는 작업을 보낸 후에는 통제할 수 없는데 반해 NSOperation
을 중지, 취소, 재게할 수 있다. 또한, 동시에 수행될 최대 작업의 수를 지정할 수 있다.
- Observable
NSOpration
, NSOperationQueue
클래스는 KVO를 사용하여 관찰(observe)될 수 있는 여러 프로퍼티를 가진다.