2014-12-13 26 views
5

Reaktif Uzantıları kullanarak belirli bir zamanda bir işlevi çağırmak mümkün mü? Her gün, tam 9 ve 1 de yöntem foo() aramak istiyorsanızC# Reaktif Uzantıları ile belirli bir zamanda bir işlevi çağırın

Örneğin, ben 9 am ya 1 de, hatta Observable.Interval fonksiyonunu ise her birkaç saniyede bir kontrol etmek Timer sınıfını kullanabilirsiniz. Ama bunu yapmanın daha verimli bir yolu var mı? Bu yüzden foo() 'yı arama zamanı geldiğinde her birkaç saniyede bir kontrol yapıyorum, ancak tam olarak kendi zamanında foo()' yı arayacak bir gözlemlenebilir.

+0

Bir sonraki tetikleyiciye kadar olan zamanı hesaplayabilir ve bu zaman aşımına sahip bir zamanlayıcı kullanabilirsiniz ([Threading.Timer] içinde http://www.msdn.microsoft.com/en- us/library/3yb9at7c (v = vs.110) .aspx)) – Jcl

+4

Kodunuzu Windows Görev Zamanlayıcısını kullanabileceğiniz şekilde yazın. d için. – JRLambert

cevap

5

Yalnızca DateTimeOffset değerini kabul eden Timer aşırı yüklenmesini kullanın. Bir "mutlak aralık" oluşturmak için Defer ve Repeat'u kullanabilirsiniz. Bildirim bile zamanlayıcı sürüklenme açısından ve zamanlayıcı süresi öncesinde sistem saati değişiklikleri geçtikten, belirtilen kesin bir tarih ve saatte gerçekleşecek yönündeki elverdiğince

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,.

Sorunu daha da genelleştiren bir uzantı yöntemi.

Kullanımı:

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

Tanımı:

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

Güncelleme:

Eğer giriş tanımlayarak yaklaşımı daha "işlevsel" olmasını istiyorsanız Bir yineleyiciden sonsuz bir sıra olarak Jeroen Mostert'ın cevabı uyarınca, aşağıdaki gibi Generate kullanabilirsiniz.

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

Jeroen Mostert cevabı ( silinmiş) GetScheduleTimes bir örnek uygulamasını temin ama temelde değerleri artan her döngü ile bir süre döngü içinde DateTimeOffset değerlerinden sonsuz serisini vermiştir sadece bir yineleyici blok gün tarafından 1.

İlgili konular