2012-12-12 19 views
15

C# (.NET 4.0) uygulamasında, olayları toplu değerlere ayırmak için zaman sınırları oluşturmak için Reaktif Uzantıları (2.0.20823.0) kullanın. Ortaya çıkan veri tabanındaki sorguları basitleştirmek için, bu sınırların tam saatte (veya aşağıdaki örnekte saniye olarak) hizalanması gerekir. Observable.Timer() kullanmaObservable.Timer(): Zamanlayıcı sapmasından nasıl kaçınılır?

:

var time = DefaultScheduler.Instance; 

var start = new DateTimeOffset(time.Now.DateTime, time.Now.Offset); 

var span = TimeSpan.FromSeconds(1); 

start -= TimeSpan.FromTicks(start.Ticks % 10000000); 
start += span; 

var boundary = Observable.Timer(start, span, time); 

boundary.Select(i => start + TimeSpan.FromSeconds(i * span.TotalSeconds)) 
    .Subscribe(t => Console.WriteLine("ideal: " + t.ToString("HH:mm:ss.fff"))); 

boundary.Select(i => time.Now) 
    .Subscribe(t => Console.WriteLine("actual: " + t.ToString("HH:mm:ss.fff"))); 

Sen amaçlanan ve zamanlayıcı gerçek zamanlı birbirinden oldukça ağır sürüklenip keneler olduğunu görebilirsiniz: Ben de bir HistoricalScheduler ve tabii faydalanmak

ideal: 10:06:40.000 
actual: 10:06:40.034 
actual: 10:06:41.048 
ideal: 10:06:41.000 
actual: 10:06:42.055 
ideal: 10:06:42.000 
ideal: 10:06:43.000 
actual: 10:06:43.067 
actual: 10:06:44.081 
ideal: 10:06:44.000 
ideal: 10:06:45.000 
actual: 10:06:45.095 
actual: 10:06:46.109 
ideal: 10:06:46.000 
ideal: 10:06:47.000 
actual: 10:06:47.123 
actual: 10:06:48.137 
ideal: 10:06:48.000 
... 

Orada problemim yok. Hafif hataları tolere edebilirim ve sistem saati değişikliklerini önemsememe gerek yok. Gözlemciler tarafından tetiklenen ağır faaliyetler bulunmamaktadır. Ayrıca, blog post numaralı telefonun RX zaman ayarlı sürüklenme problemleri hakkında uzun bir tartışma olduğunu biliyorum, ancak kafamın etrafını saracak gibi görünmüyorum.

Ne periyodik sistematik zamanlayıcı savrulma olmaksızın bir Observable planlamak için doğru yol olurdu?

cevap

11

Sen Observable.Generate kullanabilirsiniz:

var boundary = Observable.Generate(
    0, _ => true, // start condition 
    i => ++i,  // iterate 
    i => i,  // result selector 
    i => start + TimeSpan.FromSeconds(i * span.TotalSeconds), 
    time); 

Bu, her yineleme mutlak zamana dayalı yeniden planlayın olacak.

İşte bazı örnek çıktı verilmiştir:

actual: 01:00:44.003 
ideal: 01:00:44.000 
actual: 01:00:44.999 
ideal: 01:00:45.000 
actual: 01:00:46.012 
ideal: 01:00:46.000 
actual: 01:00:47.011 
ideal: 01:00:47.000 
actual: 01:00:48.011 
ideal: 01:00:48.000 
actual: 01:00:49.007 
ideal: 01:00:49.000 
actual: 01:00:50.009 
ideal: 01:00:50.000 
actual: 01:00:51.006 
ideal: 01:00:51.000 

Ben hayal tam olarak uymuyor nedeniyle Hans açıklanabilir nedenlerle, ama hiç sürüklenme yok.

DÜZENLEME:

İşte bu bir katil cevaptır RxSource

// BREAKING CHANGE v2 > v1.x - No more correction for time drift based on absolute time. This 
//        didn't work for large period values anyway; the fractional 
//        error exceeded corrections. Also complicated dealing with system 
//        clock change conditions and caused numerous bugs. 
// 
// - For more precise scheduling, use a custom scheduler that measures TimeSpan values in a 
// better way, e.g. spinning to make up for the last part of the period. Whether or not the 
// values of the TimeSpan period match NT time or wall clock time is up to the scheduler. 
// 
// - For more accurate scheduling wrt the system clock, use Generate with DateTimeOffset time 
// selectors. When the system clock changes, intervals will not be the same as diffs between 
// consecutive absolute time values. The precision will be low (1s range by default). 
+0

Sorunumu çözmek için çok teşekkürler, mükemmel cevap ve yeterince doğru. Eğlenceli bir şekilde, bunu gözlemlemiştim çünkü 'Gözlemlenebilir.Generate()' bana [ilgili senaryoda] baş ağrıları veriyordu (http://stackoverflow.com/questions/13462713/why-does-observable-generate-throw- sistem StackOverflowException). RX kaynakları ile ilgili bir soru sorduğumda donanımımı iyi tanımak gibi görünüyor. –

+0

Tamam, RX kaynaklarındaki yorumları işaretlediğiniz için teşekkürler. Onları daha sık kontrol etmem gerekiyor, şimdi mevcut. –

26

Çoğu makinede varsayılan Windows saat kesme hızı saniyede 64 kesiliyor. CLR tarafından 15,6 milisaniye yuvarlandı. 1000 milisaniyelik bir aralık istemeniz halinde bu mutlu bir sayı değil, ayrılmaz bir bölünmez yok. En yakın eşleşme 64 x 15,6 = 998 (çok kısa) ve 65 x 15,6 = 1014 milisaniyedir. Gördüğünüz tam olarak ne

, 41,048-40,034 = 1,014. 44.081 - 43.067 = 1.014, vb.

Aslında kesme hızını değiştirebilir, timeBeginPeriod PInvoke() ve bir 1 milisaniye aralığı için sorabilirsiniz. Sıfırlamak için program sonlandırmada timeEndPeriod() gerekir. Bu tam olarak yapmak için çok makul bir şey değildir, sistem genelinde yan etkileri vardır ve güç tüketimi için çok zararlıdır. Ama problemini çözecek.

daha aklı başında bir yaklaşım sadece aralıkları ekleyerek doğru zamanı tutmak asla kabul etmektir. CLR'nin kullandığı 15.6 msn zaten bir yaklaşımdır. Mutlak saatle her zaman yeniden kalibre edin. 1000 yerine 998 msn isteyerek daha yakınlaşın. Etcetera.

+2

bazı yorumları var, gerçekten, vay harika bir cevap –

+0

upvoted. Paul’in değerlendirmesine göre, bu sınırlamanın etrafında Observable.Timer() için bir yol olmadığını varsayıyorum.Hem Matthew'inizin cevaplarını doğru olarak işaretleyebilirim. RX'e özgü olanı seçmeye karar verdim, ama yine de teşekkürler. –

İlgili konular