2012-12-12 8 views
15

C#(.NET 4.0)アプリケーションでは、Reactive Extensions(2.0.20823.0)を使用して、イベントを集計値にグループ化するための時間境界を生成します。結果のデータベースへのクエリを簡素化するために、これらの境界線は完全な時間(または下の例では秒)に整列する必要があります。Observable.Timer():タイマードリフトを回避するにはどうすればよいですか?

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

あなたが意図していることがわかりますし、タイマーの実際の時間が非常に大きく離れて漂流ティック:

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

私もHistoricalSchedulerの、そしてもちろん使用することObservable.Timer()を使用して

私はそこに問題がない。私はわずかな不正確さを許容することができ、私はシステムクロックの変更を気にする必要はありません。 Observablesによってトリガされる重量操作はありません。

また、blog postにRXタイマーのドリフトの問題についての長い議論があることは知っていますが、私はその周りを頭で囲むことができないようです。

体系的なタイマードリフトなしでObservableを定期的にスケジュールする正しい方法は何でしょうか?

答えて

11

あなたはObservable.Generateを使用することができます。

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

これは絶対時間に基づいてすべての反復を再スケジュールします。

は、ここではいくつかのサンプル出力です:

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 

それは私が想像して、正確に一致しないため、ハンスによって説明の理由が、ないドリフトがありません。

EDIT:

ここでこれはキラー答えです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

ありがとう、素晴らしい答えと私の問題を解決するために十分正確です。面白いことに、Observable.Generate()が[関連するシナリオ]で頭痛を与えているので、私はすでにそれを排除していました(http://stackoverflow.com/questions/13462713/why-does-observable-generate-throw-システムスタックオーバーフロー例外)。私はRXに関する質問をするたびに私のハードウェアをよく知るように思えます:-) –

+0

RXソースのコメントを指摘してくれてありがとう。私はもっ​​と頻繁にそれらをチェックアウトする必要があるように見える、今すぐ利用可能です。 –

26

ほとんどのマシンでのデフォルトのWindowsクロック割り込みレートは、毎秒64割り込みです。 CLRによって15.6ミリ秒に丸められます。あなたが1000ミリ秒の間隔を求めるなら、これは幸せな数字ではありません。積分除数はありません。最も近い一致は、64×15.6 = 998(短すぎる)および65×15.6 = 1014ミリ秒である。

あなたが見ているものは、41.048 - 40.034 = 1.014です。 44.081 - 43.067 = 1.014など。

実際に割り込みレートを変更することができます。timeBeginPeriod()をピンボケして1ミリ秒の間隔を要求することができます。それをリセットするには、プログラムの終了時にtimeEndPeriod()が必要です。これはまさに非常に合理的なことではなく、システム全体の副作用があり、電力消費に非常に有害です。しかし、あなたの問題を解決します。

もっと元気なアプローチは、間隔を追加することによって決して正確に時間を保つことができないことをちょうど認めることです。 CLRが使用する15.6 msecはすでに近似値です。常に絶対時計で再較正する。近いうちに1000の代わりに998ミリ秒を求めてください。

+2

からいくつかのコメントがあります、実際うわー、素晴らしい答えを –

+0

upvoted。ポールのアップヴォートで判断すると、この制限を回避するためのObservable.Timer()の方法はないと私は考えている。あなたとマシューの答えを正しいものにすることを願っています。私はRX特有のものと一緒に行くことに決めましたが、それでもなお感謝します。 –

関連する問題