Std::This_Thread::Sleep_For Sleeps for Too Long

std::this_thread::sleep_for sleeps for too long

In short, the problem is that you use std::this_thread::sleep_for at all. Or, any kind of "sleep" for that matter. Sleeping to limit the frame rate is just utterly wrong.

The purpose of sleep functionality is, well... I don't know to be honest. There are very few good uses for it at all, and in practically every situation, a different mechanism is better.

What std::this_thread::sleep_for does (give or take a few lines of sanity tests and error checking) is, it calls the Win32 Sleep function (or, on a different OS, a different, similar function such as nanosleep).

Now, what does Sleep do? It makes a note somewhere in the operating system's little red book that your thread needs to be made ready again at some future time, and then renders your thread not-ready. Being not-ready means simply that your thread is not on the list of candidates to be scheduled for getting CPU time.

Sometimes, eventually, a hardware timer will fire an interrupt. That can be a periodic timer (pre Windows 8) with an embarrassingly bad default resolution, or programmable one-shot interrupt, whatever. You can even adjust that timer's resolution, but doing so is a global thing which greatly increases the number of context switches. Plus, it doesn't solve the actual problem. When the OS handles the interrupt, it looks in its book to see which threads need to be made ready, and it does that.

That, however, is not the same as running your thread. It is merely a candidate for being run again (maybe, some time).

So, there's timer granularity, inaccuracy in your measurement, plus scheduling... which altogether is very, very unsuitable for short, periodic intervals. Also, different Windows versions are known to round differently to the scheduler's granularity.

Solution: Do not sleep. Enable vertical sync, or leave it to the user to enable it.

std::this_thread::sleep_for sleeps shorter than expected in VS2015

The sleep_for() method implementation in VS2015 already contains a loop around the Sleep() system call, so any spurious wakeups will not affect it - see _Thrd_sleep() in VC\crt\src\stl\cthread.c.


The cause of your problems is most probably the fact that sleep_for(), and sleep_until() it calls inside, are using chrono::system_clock to calculate the time to wait, but you are measuring the period using chrono::steady_clock.

Both timers might have the same precision, but not necessarily the same accuracy. Then it can happen that the system_clock is lagging a little bit, while steady_clock is already a few μs ahead, and the wait calculated using system_clock is actually shorter than requested.


In this exact case, steady_clock is implemented using QueryPerformanceCounter() (see VC\include\chrono, search for struct steady_clock), so it will be very accurate and precise, as that's the preferred Windows API to use for exact time measurements.

system_clock is implemented using GetSystemTimePreciseAsFileTime() (see VC\include\chrono, search for struct system_clock), which promises "the highest possible level of precision (<1us)" also. Alas, as the MSDN page says, this API is supported only from Windows 8 up and you are stuck with the older GetSystemTimeAsFileTime() with just ms precision on your Windows 7 machine. And that is most probably the result of your measurement errors.


Depending on your exact use case, you might deal with this in different ways of course. I'd consider just ignoring the small error, as suspending the thread and waiting for the scheduler to wake it up will not be very accurate anyway.

this_thread::sleep_for sleeps early

printf doesnt necessarily flush the buffer, which is why you dont see it until the thread resumes from sleep (it does this because it notices the thread is inactive). To force a flush add fflush(stdout); before the call to thread sleep.

Unpredictable behaviour of std::sleep_for on Windows 10

This answer is created from Jeremy Friesner's comment. He suggests to try timeBeginPeriod(1) at the beginning of the main(). That worked out! I created a modification of the program, and it runs 10 times faster.
Here is the code:

#include <thread>
#include <iostream>
#include <chrono>
#include <windows.h>

using namespace std;

int main(int argc, char const *argv[])
{
timeBeginPeriod(1);
unsigned t = 0;
auto start = ::std::chrono::steady_clock::now();
for(unsigned i = 0; i < 100; ++i) {
::std::this_thread::sleep_for(chrono::milliseconds(1));
t ^= i; // prevent overeager optimization
}
auto stop = ::std::chrono::steady_clock::now();
auto elapsed = ::std::chrono::duration_cast<::std::chrono::milliseconds>(stop - start);
::std::cout << elapsed.count() << "\n";
::std::cerr << t << "\n";

return 0;
}

It can be compiled using the following command:

cl /EHsc /std:c++17 /O2 sleep_for.cpp winmm.lib

Thanks, Jeremy!

P.S.
Increasing timer frequency has its price:
https://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/

boost::this_thread::sleep_for sleeping entire program

it hangs there and sleeps all the other threads as well

No it doesn't. If it seems that way, that is because the other threads were already stuck or finished.

Look for things that block (mutex.lock, condition wait, IO operations, etc.) or check that the threads didn't exit.

Notes

  1. Your seconds calculations is off. On my system, the following:

    Live On Coliru

    #include <boost/chrono.hpp>
    #include <iostream>

    int main() {
    std::cout << boost::chrono::duration<long, boost::ratio<60> >(1)/boost::chrono::seconds(1) << std::endl;
    }

    Prints

    60

    So, what you named seconds is actually minutes. Just do this instead:

    using boost::chrono::seconds;
    int delay = std::strtoul(getId().c_str(), NULL, 10)*10;
    sleep_for(seconds(delay));
  2. Your random delay is only random if the getId return is. Using boost/random.hpp you can make it truly random, with good range control. E.g. to sleep between 1'000 and 3'000 ms:

    int random_gen(int low, int high) { // not threadsafe
    static boost::random_device rdev;
    static boost::mt19937 prng(rdev);
    return boost::uniform_int<>(low, high)(prng);
    }

    void Upload::Wait() {
    int const ms_delay = random_gen(1000, 3000);
    cout << "RANDOM WAIT TIME = " << ms_delay << endl;
    sleep_for(milliseconds(ms_delay));
    }

    Note to seed using random_device as shown (so true random seed) you need to link the random library. Otherwise, you can "stoop" to a time-based seed:

     static boost::mt19937 prng(std::time(NULL));

Here's a self-contained version of your code with the various suggestions applied, demonstrating that there is no deadlock/softlock:

Live On Coliru

#include <boost/asio.hpp>
#include <boost/chrono.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include <boost/random.hpp>

using boost::this_thread::sleep_for;
using boost::chrono::seconds;
using boost::chrono::milliseconds;

using boost::lexical_cast;
using std::cout;
using std::endl;

struct Upload {
std::string getId() const { return "42"; }
void Wait();
void ReadFileNames();
};

void Upload::ReadFileNames() {
cout << "[DEBUG] ReadFileNames -> A " << endl;
Wait();
cout << "[DEBUG] ReadFileNames -> B " << endl;

// read filename stuff
}

int random_gen(int low, int high) { // not threadsafe
static boost::mt19937 prng(std::time(NULL));
return boost::uniform_int<>(low, high)(prng);
}

void Upload::Wait() {
int const ms_delay = random_gen(1000, 3000);
cout << "RANDOM WAIT TIME = " << ms_delay << endl;
sleep_for(milliseconds(ms_delay));
}

void background(char const* name) {
// desync different background threads
sleep_for(milliseconds(boost::hash_value(name) % 1000));

for (int i=0; i<5; ++i) {
sleep_for(seconds(1));
std::clog << name << " " << i << std::endl;
}
}

void threadAfn() { background("thread A"); }
void threadBfn() { background("thread B"); }

void Uploadfn() {
Upload u;
u.ReadFileNames();
}

int main() {
boost::thread threadA(threadAfn);
boost::thread threadB(threadBfn);
boost::thread Upload(Uploadfn);

threadA.join();
threadB.join();
Upload.join();
}

Prints, e.g.:

[DEBUG] ReadFileNames -> A 
RANDOM WAIT TIME = 1150
[DEBUG] ReadFileNames -> B
thread A 0
thread B 0
thread A 1
thread B 1
thread A 2
thread B 2
thread A 3
thread B 3
thread A 4
thread B 4


Related Topics



Leave a reply



Submit