2012-12-12 35 views
15

Trong ứng dụng C# (.NET 4.0), tôi sử dụng phần mở rộng phản ứng (2.0.20823.0) để tạo ra các ranh giới thời gian để nhóm các sự kiện thành các giá trị tổng hợp. Để đơn giản hóa các truy vấn đến cơ sở dữ liệu kết quả, các ranh giới này cần phải được căn chỉnh theo giờ đầy đủ (hoặc giây trong ví dụ bên dưới).Observable.Timer(): Làm thế nào để tránh trôi hẹn giờ?

Sử dụng 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"))); 

Bạn có thể thấy rằng các dự định và thời gian thực tế của bộ đếm thời gian ve trôi dạt ngoài khá nặng nề:

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

Tôi cũng tận dụng một HistoricalScheduler và tất nhiên Tôi không có vấn đề ở đó. Tôi có thể chịu đựng được những điểm không chính xác nhỏ và tôi không cần phải quan tâm đến những thay đổi của đồng hồ hệ thống. Không có hoạt động hạng nặng nào được kích hoạt bởi các Đài quan sát đó.

Ngoài ra, tôi biết có một cuộc thảo luận dài về vấn đề thời gian trôi dạt RX trong số blog post này, nhưng dường như tôi không thể quấn đầu xung quanh nó.

Điều gì sẽ là đúng cách để định kỳ lập lịch biểu Observable mà không có sự cố định thời gian có hệ thống?

Trả lời

11

Bạn có thể sử dụng 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); 

này sẽ sắp xếp lại dựa trên thời gian tuyệt đối mỗi lần lặp.

Dưới đây là một số lượng mẫu:

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 

Nó không phù hợp chính xác, tôi tưởng tượng vì lý do giải thích bởi Hans, nhưng không có trôi dạt.

EDIT:

Dưới đây là một số ý kiến ​​từ 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

Cảm ơn rất nhiều, câu trả lời xuất sắc và đủ chính xác để giải quyết vấn đề của tôi. Ngạc nhiên thay, tôi đã loại trừ điều đó bởi vì 'Observable.Generate()' đã khiến tôi đau đầu trong một [kịch bản có liên quan] (http://stackoverflow.com/questions/13462713/why-does-observable-generate-throw- system-stackoverflowexception). Có vẻ như tôi biết phần cứng của tôi khá tốt mỗi khi tôi hỏi một câu hỏi liên quan đến RX :-) –

+0

Ok, cảm ơn bạn đã chỉ ra các nhận xét trong các nguồn RX. Có vẻ như tôi nên kiểm tra chúng thường xuyên hơn, bây giờ chúng có sẵn. –

26

Tốc độ ngắt đồng hồ Windows mặc định trên hầu hết các máy là 64 ngắt mỗi giây. Làm tròn bởi CLR đến 15,6 mili giây. Đó không phải là một con số hạnh phúc nếu bạn yêu cầu một khoảng thời gian 1000 mili giây, không có ước số tích phân. Các kết quả phù hợp gần nhất là 64 x 15,6 = 998 (quá ngắn) và 65 x 15,6 = 1014 mili giây.

Đó chính xác là những gì bạn đang thấy, 41.048 - 40.034 = 1.014. 44.081 - 43.067 = 1.014, v.v.

Bạn thực sự có thể thay đổi tốc độ ngắt, bạn có thể ghim timeBeginPeriod() và yêu cầu khoảng thời gian 1 mili giây. Bạn sẽ cần timeEndPeriod() khi chấm dứt chương trình để thiết lập lại nó. Đây không phải là một điều rất hợp lý để làm, nó có tác dụng phụ toàn hệ thống và rất bất lợi cho việc tiêu thụ điện năng. Nhưng sẽ giải quyết vấn đề của bạn.

Một cách tiếp cận lành mạnh hơn là chỉ cần thừa nhận rằng bạn không bao giờ có thể giữ thời gian chính xác bằng cách thêm khoảng thời gian. 15,6 msec mà CLR sử dụng đã gần đúng. Luôn luôn hiệu chỉnh lại với đồng hồ tuyệt đối. Đến gần hơn bằng cách yêu cầu 998 msec thay vì 1000. Etcetera.

+2

Đây là một câu trả lời kẻ giết người, bỏ phiếu tán –

+0

Wow, câu trả lời thực sự vĩ đại. Đánh giá bởi upvote của Paul tôi giả định không có cách nào cho 'Observable.Timer()' xung quanh giới hạn này.Ước gì tôi có thể đánh dấu cả câu trả lời của bạn và của Matthew là chính xác. Tôi quyết định đi với RX-cụ thể, nhưng dù sao. –

Các vấn đề liên quan