2014-12-13 2 views
5

Reactive Extensions를 사용하여 특정 시간에 함수를 호출 할 수 있습니까?C# Reactive Extensions로 특정 시간에 함수 호출

예를 들어, 매일 오전 9시와 오후 1시에 foo() 메서드를 호출하려면 매일 Timer 클래스를 사용하여 오전 9시 또는 오후 1시 또는 심지어 Observable.Interval 함수인지 확인합니다. 그러나 이것을하기위한보다 효율적인 방법이 있습니까? 그래서 foo()를 호출 할 시간이되면 몇 초마다 검사하지 않고 적절한 시간에 foo()를 호출 할 관찰 할 수 있습니다.

+0

다음 트리거까지의 시간을 계산하고 해당 시간 초과가있는 타이머를 사용합니다 ([Threading.Timer]의 매개 변수 'dueTime'확인 (http://msdn.microsoft.com/en- us/library/3yb9at7c (v = vs.110) .aspx)) – Jcl

+4

Windows 작업 스케줄러를 사용할 수있는 방식으로 코드를 작성하십시오. d를 위해. – JRLambert

답변

5

Timer의 오버로드를 사용하면 DateTimeOffset 값을 사용할 수 있습니다. DeferRepeat을 사용하여 "절대 간격"을 만들 수 있습니다. 알림도 타이머 드리프트과 관련하여 타이머 시간 전에 시스템 클럭 변경이 경과 한 경우, 지정된 정확한 날짜와 시간에 발생하는 것이 능력의 최선을

Observable.Defer(() => 
    DateTime.Now.Hour < 9 
    ? Observable.Timer(DateTime.Today.AddHours(9)) 
    : DateTime.Now.Hour < 13 
    ? Observable.Timer(DateTime.Today.AddHours(13)) 
    : Observable.Timer(DateTime.Today.AddDays(1).AddHours(9))) 
      .Repeat() 
      .Subscribe(...); 

수신 automatically ensures,.

다음은이 문제를 일반화하는 확장 방법입니다.

사용법 :

Observable2.Daily(TimeSpan.FromHours(9), TimeSpan.FromHours(13)).Subscribe(...); 

정의 :

public static partial class Observable2 
{ 
    public static IObservable<long> Daily(params TimeSpan[] times) 
    { 
    Contract.Requires(times != null); 
    Contract.Requires(Contract.ForAll(times, time => time > TimeSpan.Zero)); 
    Contract.Requires(Contract.ForAll(times, time => time.TotalDays < 1)); 

    return Daily(Scheduler.Default, times); 
    } 

    public static IObservable<long> Daily(IScheduler scheduler, params TimeSpan[] times) 
    { 
    Contract.Requires(times != null); 
    Contract.Requires(Contract.ForAll(times, time => time > TimeSpan.Zero)); 
    Contract.Requires(Contract.ForAll(times, time => time.TotalDays < 1)); 

    if (times.Length == 0) 
     return Observable.Never<long>(); 

    // Do not sort in place. 
    var sortedTimes = times.ToList(); 

    sortedTimes.Sort(); 

    return Observable.Defer(() => 
     { 
     var now = DateTime.Now; 

     var next = sortedTimes.FirstOrDefault(time => now.TimeOfDay < time); 

     var date = next > TimeSpan.Zero 
       ? now.Date.Add(next) 
       : now.Date.AddDays(1).Add(sortedTimes[0]); 

     Debug.WriteLine("Next @" + date + " from " + sortedTimes.Aggregate("", (s, t) => s + t + ", ")); 

     return Observable.Timer(date, scheduler); 
     }) 
     .Repeat() 
     .Scan(-1L, (n, _) => n + 1); 
    } 
} 

업데이트 :

당신이 입력을 정의하여 접근 방식에 더 많은 "기능"싶은 경우 반복자에서 무한한 시퀀스로 블록, Jeroen Mostert의 답변에 따라 다음과 같이 Generate을 사용할 수 있습니다.

Observable.Generate(
    GetScheduleTimes().GetEnumerator(), 
    e => e.MoveNext(), 
    e => e, 
    e => e.Current, 
    e => e.Current); 

제론 Mostert의 답변 ( 삭제) GetScheduleTimes의 예시적인 구현을 제공하지만, 기본적으로이 값을 '증가 각각 루프, while 루프에 DateTimeOffset 값 무한 시퀀스를 수득 단지 반복기 블록이었다 일을 기준으로 계산합니다.