Xiaoy312 has a point about the risk of missing seconds when using Interval
. It seems that each "tick" of Observable.Interval()
waits until the previous returns. So if the OnNext halts the thread a while, the next "tick" is fired too late and you may miss a second or two.
Trying to start each scheduled task on a new thread - for instance via a System.Timers.Timer
seems to be a bad idea, since the same Cron job probably can not run concurrently(?).
One way not to lose any seconds using Observable.Interval
is as follows:
Random rand = new Random(5);
IObservable<Timestamped<long>> source = Observable.Interval(TimeSpan.FromSeconds(1)).Timestamp().ObserveOn(NewThreadScheduler.Default);
IDisposable subscription = source.Subscribe(
x =>
{
Console.WriteLine("OnNext: {0}", x);
Thread.Sleep(rand.Next(0, 4001));
Console.WriteLine("After Sleep OnNext: {0}", x);
},
ex => Console.WriteLine("OnError: {0}", ex.Message),
() => Console.WriteLine("OnCompleted"));
Console.WriteLine("Press ENTER to unsubscribe...");
Console.ReadLine();
subscription.Dispose();
This seems to ensure no slip in the sequence of timestamps (Second-wise at least), but each elapsed interval may be (cumulatively) delayed according to previous intervals "laziness". In this way you are not guarantied that the job is done on schedule, but you won't miss a job because of "missing" seconds. It seems though that the time pump catches up the delay when no action delay the current elapsed interval.
Update
The above solution doesn't solve the millisecond alignment problem described by Xiaoy312.
I think a simple way to solve that could be at simple as this:
public static IObservable<DateTime> Create(TimeSpan interval, DateTime dateTime)
{
return
Observable
.Interval(TimeSpan.FromSeconds(1))
.Select(_ =>
{
DateTime now = DateTime.Now;
return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, 0);
});
}
This should of cause be implemented in the appropriate IDateTime
implementation.