RxSwift(9) - Scheduler

2025. 5. 9. 15:57·반응형프로그래밍

🧐Scheduelr?

Swift에서 멀티스레딩은 주로 GCD를 통해 처리합니다. 반면 RxSwift에서는 Scheduelr라는 추상화된 개념을 사용합니다.

하지만 scheduler != thread

Scheduler와 쓰레드 관계

  • 하나의 쓰레드에 여러개의 스케쥴러가 존재할 수 있고
  • 여러개의 쓰레드에 하나의 스케쥴러가 존재할 수 있습니다.

Scheduler종류

CurrentThreadScheduelr

public class CurrentThreadScheduler : ImmediateSchedulerType {
    public static let instance = CurrentThreadScheduler()
}

: 기본 스케쥴러로, Serial Schduler로 동작합니다. 

MainScheduler

public final class MainScheduler : SerialDispatchQueueScheduler {
    public static let instance = MainScheduler()
    public static let asyncInstance = SerialDispatchQueueScheduler(serialQueue: DispatchQueue.main)
}
  • UI작업에 사용되는 가장 일반적인 스케쥴러
  • Serial Scheduler(GCD Main Queue 유사)
  • instance: 동기 방식의 싱글톤 인스턴스(serial & sync)/ asyncInstance: 비동기방식의 싱글톤 인스턴스(serial & async)
  • ObserveOn연산자에 최적화 

SerialDispatchQueueScheduler & ConcurrentDispatchQueueScheduler

public class SerialDispatchQueueScheduler : SchedulerType { /* ... */ }
public class ConcurrentDispatchQueueScheduler: SchedulerType { /* ... */ }
  • 백그라운드 작업이나 특정 디스패치 큐를 지정할 때 사용
  • GCD 큐를 래핑한 형태로 볼 수도 있다.

OperationQueueScheduler

  • 실행 순서 제어나 동시 실행 작업 수 제한이 필요할 때 사용
  • OperationQueue 추상화 버전

Observable과 Scheduler의 관계

Rx에서 중요한 개념은 Observable이 구독되는 시점에 생성된다는 것이다.

// 이 코드만으로는 아무것도 실행되지 않습니다
Observable.of(1, 2, 3, 4, 5)
    .map { $0 * 2 }

// subscribe 호출 시 Observable이 생성되고 실행됩니다
Observable.of(1, 2, 3, 4, 5)
    .map { $0 * 2 }
    .subscribe(onNext: { number in
        print(number)
    })
    .disposed(by: disposeBag)

기본적으로 Observable은 별도 지정이 없으면 main thread에서 동작합니다. RxSwift에서는 두 가지 방법으로 스케줄러를 변경할 수 있습니다.

Scheduler 제어 연산자

observeOn

해당 연산자 이후의 체인에서 사용할 스케쥴러를 지정합니다. 위치가 중요한 연산자!

subscribeOn

Observable이 생성되는 스케쥴러를 지정합니다. 이 연산자는 체인 내 어디에 위치하든 동일한 효과를 가집니다. 자 예시로 넘어가서 더 이해해봅시당

let backgroundScheduler = ConcurrentDispatchQueueScheduler(queue: DispatchQueue.global())

Observable.of(1, 2)
    .do { number in
        if Thread.isMainThread {
            print("[main]: do", number)  // 실행됨 ✓
        } else {
            print("[not main]: do", number)
        }
    }
    .compactMap({ number -> Int? in
        if Thread.isMainThread {
            print("[main]: compactMap", number)   // 실행됨 ✓
        } else {
            print("[not main]: compactMap", number)
        }
        return number
    })
    .observe(on: backgroundScheduler) // 여기서부터 백그라운드 스케줄러 적용
    .filter { number -> Bool in
        if Thread.isMainThread {
            print("[main]: filter", number)
        } else {
            print("[not main]: filter", number)  // 실행됨 ✓
        }
        return true
    }
    .map { number -> Int in
        if Thread.isMainThread {
            print("[main]: map", number)
        } else {
            print("[not main]: map", number)  // 실행됨 ✓
        }
        return number
    }
    .subscribe(on: MainScheduler.instance) // Observable 생성 스케줄러 지정
    .subscribe(onNext: { number in
        if Thread.isMainThread {
            print("[main]: subscribe", number)
        } else {
            print("[not main]: subscribe", number)  // 실행됨 ✓
        }
    })
    .disposed(by: disposeBag)

observeOn이전의 연산자들(do, compactMap)은 subscribeOn에서 지정한 MainScheduelr에서 실행됩니다.

observeOn이후의 연산자들(filter. map, subscribe)은 backgroundScheduler에서 실행됩니다. 

  • 메인 스케쥴러에서는 메인스레드에서 직렬로 작업을 처리하겠죠. 즉 1에대한 모든 작업이 완료된 후에 2에 대한 작업을 시작할 것입니다. 예상 가능한 순차적 실행으로
  • backgroundScheduler에서는 여러 쓰레드에서 동시에 작업을 처리합니다.
    • 1의 Filter->Map->subscribe가 한 스레드에서 실행되는 동안 다른 쓰레드에서는 2의 작업이 병렬로 실행될수 있습니다. 즉 실행 순서가 항상 일정하지 않죠

🚩 Scheduler 사용 시 주의사항

  1. 적절한 스케줄러 선택:
    • UI 작업 → MainScheduler
    • 무거운 계산 → ConcurrentDispatchQueueScheduler
    • API 호출, 파일 작업 → 백그라운드 스케줄러
  2. observeOn의 위치:
    • 위치에 따라 전체 체인의 동작이 달라짐
    • UI 업데이트 직전에 .observe(on: MainScheduler.instance)를 배치
  3. 스케줄러 전환 비용:
    • 스케줄러 간 전환은 비용이 발생함
    • 불필요한 전환은 피하고 필요한 위치에서만 사용

'반응형프로그래밍' 카테고리의 다른 글

RxSwift(8)-에러처리  (0) 2025.05.08
RxSwift(7)-BehaviorRelay  (0) 2025.05.06
RxSwift(6)-RxCocoa(bind, drive, DelegateProxy)  (0) 2025.05.06
RxSwift(5)-TimeBasedOperators  (0) 2025.05.04
RxSwift(4)-Combining Operators  (0) 2025.05.02
'반응형프로그래밍' 카테고리의 다른 글
  • RxSwift(8)-에러처리
  • RxSwift(7)-BehaviorRelay
  • RxSwift(6)-RxCocoa(bind, drive, DelegateProxy)
  • RxSwift(5)-TimeBasedOperators
2료일
2료일
좌충우돌 모든것을 다 정리하려고 노력하는 J가 되려고 하는 세미개발자의 블로그입니다. 편하게 보고 가세요
  • 2료일
    GPT에게서 살아남기
    2료일
  • 전체
    오늘
    어제
    • 분류 전체보기 (120)
      • SWIFT개발 (29)
      • 알고리즘 (25)
      • Design (6)
      • ARkit (1)
      • 면접준비 (30)
      • UIkit (2)
      • Vapor-Server with swift (3)
      • 디자인패턴 (5)
      • 반응형프로그래밍 (12)
      • CS (3)
      • 도서관 (1)
  • 인기 글

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
2료일
RxSwift(9) - Scheduler
상단으로

티스토리툴바