views:

372

answers:

4

I have this code:

    public void replay() {
        long previous = DateTime.Now.Ticks;
        for (int i = 0; i < 1000; i++) {
            Thread.Sleep(300);
            long cur = DateTime.Now.Ticks;
            Console.WriteLine(cur - previous);
            previous = cur;
        }
    }

Which is invoked as a separate thread like this:

        MethodInvoker replayer = new MethodInvoker(replay);
        replayer.BeginInvoke(null, null);

However, if I watch the output, it acts strangely. It outputs i in pairs. For example, it'll wait a full wait, then output i, then quickly output the next i too, then wait again. Why is it doing that and how can I correct it?

It outputs this:

3125040
2968788
2968788
2968788
3125040
2968788
2968788
2968788
3125040
2968788
2968788
2968788
3125040

If I increase the sleep to more than a second this doesn't happen.

A: 

Your sleep inside the loop is only 300ms, which isn't very long. You application will do the following:

  • Sleep 5 secs
  • print 0
  • Sleep 300ms
  • print 1
  • Sleep 300ms
  • print 2

etc.

ck
But it's not, and that's the problem.
Malfist
+5  A: 

Cannot reproduce. I wonder if it is something local to your machine; buffering, perhaps.

Marc Gravell
Can't reproduce here either.
LukeH
ditto. bring up task manager and watch your processes. I suspect something's eating up cycles.
Michael Meadows
suggest the OP write the output to something else - a Dictionary<Int,DateTime> perhaps - to double check
annakata
updated the question to show more data. I'm on a quad core, and most of the CPU isn't utilized.
Malfist
and what's wrong with the output you're getting?
lc
read the question and you'll know. The problem is that it's not sleeping evenly.
Malfist
but you're getting just about 300 ms each output, no?
lc
but I need it to be even, because I'm sleeping to let something render, and then capturing it.
Malfist
The difference in sleep times is minimal, and there's no real way to change that. It looks like it's behaving properly to me.
Herms
Agreed - I don't think there's anything "wrong" here. The "I need it to be even because..." changes the question - I would suggest using a timer or sleep the remaining time (Thread.Sleep(600 - (DateTime.Now.Ticks - previous) / 10000)) to be more accurate...
lc
Or, you may find the article http://www.dotnet247.com/247reference/msgs/57/289291.aspx to be of interest.
lc
There's no way to get it THAT even ( with variance less than the Operating systems context switching interval)
Charles Bretana
If you run it with sleep set to, say, 50,000 ms, you will probably see the same ( ~ 15-20 ms) variance... It's just unavoidable - (at least on any OS that implements pre-emptive multi-Processing)
Charles Bretana
+7  A: 

Change the code to eliminate display latency in your analysis:

public void replay() 
{        
    Thread.Sleep(5000);
    DateTime start = DateTime.Now;      
    for (int i = 0; i < 1000; i++) 
    {            
          Console.WriteLine(string.Format("Exec:{0} - {1} ms",
                i, DateTime.Now - start));
          start = DateTime.Now;
          Thread.Sleep(300);        
    }
}

Looking at your modified output, there is less than 5% variance (15ms out of the 300) in the loop delay. This is normal, due to the uncertainties involved in when the OS actually assigns timeslices to the thread... (If I recall correctly, in a windows OS, this is normally only every 20 ms !)

The larger discrepancy you perceive in the console output is almost certainly due to display latencys.

Charles Bretana
+2  A: 

I can't reproduce this, but you might want to consider a timer. It would be more reliable.

public class Counter
{
    private readonly TimeSpan initialDelay, incrementDelay;
    private readonly int maxCount;
    private Timer timer;
    private int count;

    public Counter(TimeSpan initialDelay, TimeSpan incrementDelay, int maxCount)
    {
        this.maxCount = maxCount;
        this.initialDelay = initialDelay;
        this.incrementDelay = incrementDelay;
    }

    public void Start(Action<int> tickBehavior)
    {
        if (timer != null)
        {
            Timer temp = timer;
            timer = null;
            temp.Dispose();
        }
        timer = new Timer(() =>
            {
                tickBehavior(count++);
                if (count > maxCount) timer.Dispose();
            }, null, initialDelay, incrementDelay);
    }
}

Use it:

Counter counter = new Counter(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(.3), 1000);
counter.Start((count) => Console.WriteLine(count););

EDIT

I'm using System.Threading.Timer, but Counter could be easily be modified to use System.Timers.Timer or System.Windows.Forms.Timer depending on your need. See this link for a description of when to use which timers.

Michael Meadows
+1 that piece of code is the coolest thing I've seen all week :)
SomeMiscGuy