Foitn
Foitn

Reputation: 746

Async sleep in loop

So for a project of mine I need to fetch values from a piece of hardware every 100 ms. The fetching takes x amount of milliseconds. So what I'm looking for but cannot find is a way to make sure a for/while always takes 100 ms (or more).

So in pseudo code:

for (int i = 0; i < 100; i++) // Take measurements for 10 seconds
{
    // Call some async method that sleeps for 100 ms
    this.temperatureList.Add(this.HardwareManager.GetSensorValue(ESensor.Temperature)); // Takes 15 ms
    this.temperatureList.Add(this.HardwareManager.GetSensorValue(ESensor.Pressure)); // Takes 30 ms
    // await async method to finish
}

I have tried some async await things with this but I cannot seem to comprehend it. Anyone who can help me find a reliable way to get 10 seconds of measurements with an interval of 100 ms?

EDIT: I know that since I'm running C# on Windows, timing is not really a valid option. But this does not really matter in my question. My question is, how can I make sure the for loop takes at least(!!!) a 100 ms, while being as close as possible to 100 ms.

Upvotes: 4

Views: 1841

Answers (1)

Lasse V. Karlsen
Lasse V. Karlsen

Reputation: 391276

One way of doing it would be to create a Stopwatch and just wait for it to reach the next milestone after doing each operation.

The benefit of this way is that:

  • You will add the overhead of the loop into the mix, adjusting your delay to compensate
  • As such, it should have little to no "drifting" (where operations will get progressively "later" according to when they should've happened)

Sort of like this:

var sw = Stopwatch.StartNew();
long next = 100;
while (sw.ElapsedMilliseconds < 10000)
{
    // do your operation here
    long left = next - sw.ElapsedMilliseconds;
    if (left > 0)
        await Task.Delay((int)left);
    next += 100;
}

Due to the accuracy of things, your operations will around each 100 milliseconds, but they shouldn't drift.

If you're OK with tying up a thread for the duration of those 10 seconds, you could switch out the delay part with Thread.Sleep:

if (left > 0)
    Thread.Sleep((int)left);

It will run your operations closer to the 100 millisecond mark, but might still have a slight overhead due to the fact that Thread.Sleep has some overhead as well.

If you're OK with "burning CPU", you can do this instead:

var sw = Stopwatch.StartNew();
var next = 100;
while (sw.ElapsedMilliseconds < 10000)
{
    // do your operation here
    
    while (sw.ElapsedMilliseconds < next) ;
    next += 100;
}

This will run your operation even closer to the actual 100 milliseconds mark, as Task.Delay and Thread.Sleep will have overhead that will tend to delay just slightly longer than what you intended. It will, however, tie up a CPU core for the duration of those 10 seconds.

Upvotes: 7

Related Questions