2014-12-13 10 views
5

Có thể gọi một hàm tại một thời điểm nhất định, bằng cách sử dụng Tiện ích phản ứng không?Gọi hàm tại một thời điểm nhất định với C# Reactive Extensions

Ví dụ: nếu tôi muốn gọi phương thức foo() vào đúng 9 giờ sáng và 1 giờ chiều, tôi có thể sử dụng lớp Timer để kiểm tra vài giây một lần nếu là 9 giờ sáng hoặc 1 giờ chiều hoặc thậm chí là chức năng Observable.Interval. Nhưng liệu có cách nào hiệu quả hơn để làm điều này? Vì vậy, tôi không kiểm tra mỗi vài giây nếu đó là thời gian để gọi foo() được nêu ra, nhưng đúng hơn là một quan sát mà sẽ gọi foo() ngày của riêng mình vào đúng thời điểm.

+0

Bạn có thể tính toán thời gian cho đến lần kích hoạt tiếp theo và sử dụng bộ hẹn giờ với thời gian chờ đó (kiểm tra tham số 'dueTime' trong [Threading.Timer] (http://msdn.microsoft.com/en- us/library/3yb9at7c (v = vs.110) .aspx)) – Jcl

+4

Viết mã của bạn theo cách mà bạn có thể sử dụng Windows Task Scheduler vì đây là những gì nó được thiết kế d cho. – JRLambert

Trả lời

5

Chỉ cần sử dụng quá tải Timer chấp nhận giá trị DateTimeOffset. Bạn có thể sử dụng DeferRepeat để tạo "khoảng thời gian tuyệt đối".

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, đến hết khả năng của mình, mà thông báo của bạn sẽ xảy ra vào ngày chính xác và thời gian nhất định, ngay cả với đối với timer trôi dạt và nếu những thay đổi hệ thống đồng hồ trước thời gian hẹn giờ đã trôi qua.

Dưới đây là một phương pháp tiện ích mở rộng tổng quát vấn đề hơn nữa.

Cách sử dụng:

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

Định nghĩa:

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

Cập nhật:

Nếu bạn muốn có nhiều "chức năng" trong cách tiếp cận của bạn bằng cách xác định đầu vào như một chuỗi vô hạn từ một trình lặp chặn, theo câu trả lời của Jeroen Mostert, thì bạn có thể sử dụng Generate như sau.

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

câu trả lời Jeroen Mostert của (xóa) cung cấp một thực hiện ví dụ về GetScheduleTimes, nhưng về cơ bản nó chỉ là một khối iterator mà mang lại một dãy vô hạn các DateTimeOffset giá trị trong một vòng lặp while, với mỗi vòng lặp incrementing các giá trị ngày tới 1.

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