다음을 통해 공유


스케줄러 사용

스케줄러는 구독이 시작되는 시기와 알림이 게시되는 시기를 제어합니다. 세 가지 구성 요소로 구성됩니다. 먼저 데이터 구조입니다. 작업을 완료하도록 예약하면 우선 순위 또는 기타 기준에 따라 큐에 대기하기 위해 스케줄러에 배치됩니다. 또한 태스크가 실행되는 위치(예: 스레드 풀, 현재 스레드 또는 다른 앱 도메인)를 나타내는 실행 컨텍스트를 제공합니다. 마지막으로 스케줄러의 속성에 액세스하여 Now 시간 개념을 제공하는 시계가 있습니다. 특정 스케줄러에서 예약되는 작업은 해당 클록으로만 표시되는 시간을 준수합니다.

스케줄러는 또한 일상 생활에서 사용되는 실시간과 상관 관계가 없는 가상 시간(VirtualScheduler 형식으로 표시됨)의 개념을 소개합니다. 예를 들어 완료하는 데 100년이 소요되도록 지정된 시퀀스는 단 5분 만에 가상 시간에 완료되도록 예약할 수 있습니다. 이 내용은 관찰 가능한 시퀀스 테스트 및 디버깅 항목에서 다룹니다.

Scheduler 형식

Rx에서 제공하는 다양한 Scheduler 형식은 모두 IScheduler 인터페이스를 구현합니다. 이러한 각 작업은 Scheduler 형식의 정적 속성을 사용하여 만들고 반환할 수 있습니다. ImmediateScheduler(정적 Immediate 속성에 액세스)는 지정된 작업을 즉시 시작합니다. CurrentThreadScheduler(정적 CurrentThread 속성에 액세스)는 원래 호출을 수행하는 스레드에서 수행할 작업을 예약합니다. 작업은 즉시 실행되지 않지만 큐에 배치되고 현재 작업이 완료된 후에만 실행됩니다. DispatcherScheduler는 정적 Dispatcher 속성에 액세스하여 현재 Dispatcher에 대한 작업을 예약합니다. 이 작업은 Rx를 사용하는 Silverlight 개발자에게 유용합니다. 그런 다음 지정된 작업이 Silverlight의 Dispatcher.BeginInvoke() 메서드에 위임됩니다. NewThreadScheduler (정적 NewThread 속성에 액세스)는 새 스레드에서 작업을 예약하며 장기 실행 또는 차단 작업을 예약하는 데 최적입니다. TaskPoolScheduler (정적 TaskPool 속성에 액세스)는 특정 작업 팩터리에 대한 작업을 예약합니다. ThreadPoolScheduler (정적 ThreadPool 속성에 액세스)는 스레드 풀에서 작업을 예약합니다. 두 풀 스케줄러 모두 짧은 실행 작업에 최적화되어 있습니다.

스케줄러 사용

사용할 스케줄러 유형을 명시적으로 명시하지 않고 Rx 코드에서 이미 스케줄러를 사용했을 수 있습니다. 동시성을 처리하는 모든 관찰 가능한 연산자는 여러 오버로드가 있기 때문입니다. 스케줄러를 인수로 사용하는 오버로드를 사용하지 않는 경우 Rx는 최소 동시성 원칙을 사용하여 기본 스케줄러를 선택합니다. 즉, 연산자의 요구 사항을 충족하는 최소 동시성을 도입하는 스케줄러가 선택됩니다.  예를 들어 유한하고 적은 수의 메시지로 관찰 가능한 을 반환하는 연산자의 경우 Rx는 Immediate를 호출 합니다.  잠재적으로 크거나 무한한 수의 메시지를 반환하는 연산자의 경우 CurrentThread 가 호출됩니다. 타이머를 사용하는 연산자의 경우 ThreadPool 이 사용됩니다.

Rx는 최소 동시성 스케줄러를 사용하므로 성능 목적으로 동시성을 도입하려는 경우 또는 스레드 선호도 문제가 있는 경우 다른 스케줄러를 선택할 수 있습니다.  전자의 예로 특정 스레드를 차단하지 않으려면 ThreadPool을 사용해야 합니다.  후자의 예로 타이머를 UI에서 실행하려는 경우 이 경우 Dispatcher를 사용해야 합니다. 특정 스케줄러를 지정하려면 스케줄러를 사용하는 연산자 오버로드(예: Timer(TimeSpan.FromSeconds(10), Scheduler.DispatcherScheduler()))를 사용할 수 있습니다.

다음 예제에서 관찰 가능한 원본 시퀀스는 혈안이 된 속도로 값을 생성합니다. 타이머 연산자의 기본 오버로드는 ThreadPool에 OnNext 메시지를 배치합니다.

Observable.Timer(Timespan.FromSeconds(0.01))
          .Subscribe(…);

그러면 관찰자가 빠르게 대기합니다. ObserveOn 연산자를 사용하여 이 코드를 개선할 수 있습니다. 이를 통해 관찰자에게 푸시된 알림(OnNext)을 보내는 데 사용할 컨텍스트를 지정할 수 있습니다. 기본적으로 ObserveOn 연산자는 OnNext가 현재 스레드에서 가능한 한 여러 번 호출되도록 합니다. 오버로드를 사용하고 OnNext 출력을 다른 컨텍스트로 리디렉션할 수 있습니다. 또한 SubscribeOn 연산자를 사용하여 특정 스케줄러에 작업을 위임하는 프록시 관찰 가능 개체를 반환할 수 있습니다. 예를 들어 UI 집약적 애플리케이션의 경우 SubscribeOn을 사용하고 ThreadPoolScheduler에 전달하여 백그라운드에서 실행되는 스케줄러에서 수행할 모든 백그라운드 작업을 위임할 수 있습니다. 푸시되는 알림을 받고 UI 요소에 액세스하려면 DispatcherScheduler의 instance ObserveOn 연산자에 전달할 수 있습니다.

다음 예제에서는 푸시된 모든 값이 UI 스레드에 전송되도록 현재 Dispatcher에서 OnNext 알림을 예약합니다. 이는 Rx를 사용하는 Silverlight 개발자에게 특히 유용합니다.

Observable.Timer(Timespan.FromSeconds(0.01))
          .ObserveOn(Scheduler.DispatcherScheduler)
          .Subscribe(…);

ObservOn 연산자를 사용하여 관찰 가능한 시퀀스가 메시지를 생성하는 실행 컨텍스트를 변경하는 대신, 시작할 적절한 위치에 동시성을 만들 수 있습니다. 연산자가 스케줄러 인수 오버로드를 제공하여 동시성 도입을 매개 변수화할 때 올바른 스케줄러를 전달하면 ObserveOn 연산자를 사용해야 하는 위치가 줄어듭니다. 예를 들어 다음 예제와 같이 원본에서 사용하는 스케줄러를 변경하여 관찰자의 차단을 해제하고 UI 스레드를 직접 구독할 수 있습니다. 이 코드에서는 스케줄러를 사용하는 타이머 오버로드를 사용하고 instance 제공하여 Scheduler.Dispatcher 이 관찰 가능한 시퀀스에서 푸시된 모든 값이 UI 스레드에서 발생합니다.

Observable.Timer(Timespan.FromSeconds(0.01), Scheduler.DispatcherScheduler)
          .Subscribe(…);

또한 ObservOn 연산자를 사용하면 원래 관찰 가능한 시퀀스를 통해 제공되는 각 메시지에 대해 작업이 예약됩니다. 이로 인해 잠재적으로 타이밍 정보가 변경될 뿐만 아니라 시스템에 추가적인 스트레스가 가해질 수 있습니다. 다양한 실행 컨텍스트에서 실행되는 다양한 관찰 가능한 시퀀스를 구성하는 쿼리가 있고 쿼리에서 필터링을 수행하는 경우 쿼리의 뒷부분에 ObservOn을 배치하는 것이 가장 좋습니다. 이는 쿼리가 잠재적으로 많은 메시지를 필터링하고 쿼리 앞부분에 ObserveOn 연산자를 배치하면 어쨌든 필터링되는 메시지에 대한 추가 작업이 수행되기 때문입니다. 쿼리 끝에서 ObserveOn 연산자를 호출하면 성능에 미치는 영향이 가장 적습니다.

스케줄러 형식을 명시적으로 지정하는 또 다른 이점은 다음 코드에 설명된 대로 성능 목적으로 동시성을 도입할 수 있다는 것입니다.

seq.GroupBy(...)
        .Select(x=>x.ObserveOn(Scheduler.NewThread))
        .Select(x=>expensive(x))  // perform operations that are expensive on resources