2014-12-13 13 views
5

Czy można wywołać funkcję o określonej godzinie przy użyciu rozszerzeń reaktywnych?Wywołanie funkcji o określonej godzinie za pomocą rozszerzeń reaktywnych C#

Na przykład, jeśli chcę wywołać metodę foo() dokładnie o 9 rano i 1pm, codziennie, mogę użyć klasy Timer sprawdzić co kilka sekund, czy to 9 rano lub 1pm, a nawet funkcję Observable.Interval. Ale czy istnieje skuteczniejszy sposób na zrobienie tego? Więc nie sprawdzam co kilka sekund, czy nadszedł czas, aby zadzwonić do foo() jeszcze, ale raczej obserwowalne, które zadzwoni do foo() we własnym zakresie we właściwym czasie.

+0

Można obliczyć czas do następnego wyzwalacza i użyć timera z tym czasem oczekiwania (sprawdź parametr 'dueTime' w [Threading.Timer] (http://msdn.microsoft.com/en- us/library/3yb9at7c (v = vs.110) .aspx)) – Jcl

+4

Wpisz swój kod w taki sposób, aby móc korzystać z Harmonogramu zadań systemu Windows, ponieważ jest to jego nazwa d dla. – JRLambert

Odpowiedz

5

Po prostu użyj przeciążenia Timer, które akceptuje wartość DateTimeOffset. Możesz użyć Defer i Repeat, aby utworzyć "interwał bezwzględny".

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(...); 

Rx automatically ensures, do starań, że zgłoszenie będzie występować o dokładnej dacie i czasie określonym, nawet w odniesieniu do dryfu czasowego i jeżeli zmiany zegar systemowy przed upływem czasu trwania timera.

Oto metoda przedłużania, która dodatkowo zwiększa problem.

Zastosowanie:

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

Definicja:

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); 
    } 
} 

Aktualizacja:

Jeśli chcesz być bardziej "funkcjonalny" w swoim podejściu definiując wejście jako nieskończoną sekwencję z iteratora blok, za odpowiedź Jeroena Mosterta, możesz użyć Generate w następujący sposób.

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

odpowiedź Jeroen Mostert'S (usunięte) pod przykładową realizację GetScheduleTimes, ale w zasadzie tylko blok iteracyjnej, która dała nieskończoną sekwencję DateTimeOffset wartości w pętli, przy czym każda pętla jest zwiększany o wartości dzień o 1.

Powiązane problemy