1

I tried to answer another question with an Rx extension solution. While I was making the solution I found something weird.

static Random rand = new Random();

static void Main(string[] args) {
    //var obs = Observable.Interval(TimeSpan.FromMilliseconds(250)).Do<long>(i =>
    var obs = Observable.Interval(TimeSpan.FromMilliseconds(25)).Do<long>(i =>
    {
        CancellationTokenSource source = new CancellationTokenSource(25);
        //CancellationTokenSource source = new CancellationTokenSource(250);
        ReadNext(source.Token, i);
    }).Publish();
    var disp = obs.Connect();
    Console.ReadKey();
    disp.Dispose();
    Console.ReadKey();
}

static private void ReadNext(CancellationToken token, long actual) {
    int i = rand.Next(4);
    Stopwatch watch = new Stopwatch();
    watch.Start();
    for(int j = 0; j < i; j++) {
        //Thread.Sleep(100);
        Thread.Sleep(10);
        if(token.IsCancellationRequested) {
            Console.WriteLine(string.Format("method cancelled. cycles: {0}, should be 3. Now should be last (2): {1}", i, j));
            return;
        }
    }
    Console.WriteLine(string.Format("method done in {0} cycles. Preserved index: {1}. Elapsed time: {2}", i, actual, watch.ElapsedMilliseconds));
    watch.Stop();
}

There is a problem with the cancellation timeout. Somehow, when the third cycle is happening (we have already waited ~30 millisecond), the ReadNext is not cancelled every time.

Check out the printout:

method done in 1 cycles. Preserved index: 7. Elapsed time: 9
method done in 1 cycles. Preserved index: 8. Elapsed time: 9
method done in 0 cycles. Preserved index: 9. Elapsed time: 0
method cancelled. cycles: 3, should be 3. Now should be last (2): 2
method done in 1 cycles. Preserved index: 11. Elapsed time: 9
method done in 2 cycles. Preserved index: 12. Elapsed time: 19
method done in 2 cycles. Preserved index: 13. Elapsed time: 19
method done in 0 cycles. Preserved index: 14. Elapsed time: 0
method done in 2 cycles. Preserved index: 15. Elapsed time: 19
method done in 0 cycles. Preserved index: 16. Elapsed time: 0
method done in 1 cycles. Preserved index: 17. Elapsed time: 9
method cancelled. cycles: 3, should be 3. Now should be last (2): 2
method done in 1 cycles. Preserved index: 19. Elapsed time: 9
method done in 3 cycles. Preserved index: 20. Elapsed time: 29  <- bug.
method done in 2 cycles. Preserved index: 21. Elapsed time: 19
method done in 1 cycles. Preserved index: 22. Elapsed time: 9
method done in 1 cycles. Preserved index: 23. Elapsed time: 9
method done in 2 cycles. Preserved index: 24. Elapsed time: 19
method done in 2 cycles. Preserved index: 25. Elapsed time: 19
method done in 2 cycles. Preserved index: 26. Elapsed time: 19
method done in 1 cycles. Preserved index: 27. Elapsed time: 10
method done in 1 cycles. Preserved index: 28. Elapsed time: 9
method done in 3 cycles. Preserved index: 29. Elapsed time: 29  <- bug.
method done in 1 cycles. Preserved index: 30. Elapsed time: 9

Do I have to listen on some other scheduler, to be sure, that after 25 milliseconds the cancellation token is surely cancelled, or something other causing the bug?

EDIT

If I Upgrade the sleeps order by one (check commented code above), it works. The problem is with Thread.Sleep is not precise enough.

Community
  • 1
  • 1
ntohl
  • 2,067
  • 1
  • 28
  • 32
  • 1
    I think the problem is that `Thread.Sleep` is not precise enough. See this [question](http://stackoverflow.com/questions/1303667/how-accurate-is-thread-sleeptimespan) – juharr Aug 03 '15 at 13:02
  • I'm checking with increasing the sleep by an order. Can I observeOn some way, or change the `Thread.sleep`, so the waiting is on the same thread with the cancellation? – ntohl Aug 03 '15 at 13:40
  • Thread.sleep is not precise enough is confirmed. Which thread/scheduler is it uses? – ntohl Aug 03 '15 at 14:31

1 Answers1

2

Windows is not a real-time OS. In general, system timers run at 60Hz, which means they are only accurate to 16.7ms. Add in the fact that are are many many more threads than there are physical cores to run them, and you just cannot expect to write code that has precise timing without really knowing what you are doing.

So, whenever writing timer code, just assume any timers you start will fire with an accuracy of +/- 16ms.

Brandon
  • 38,310
  • 8
  • 82
  • 87