简体   繁体   中英

Are there any platform-specific limitations to `std::this_thread::sleep_for()`?

Are there any known portability issues to consider when using std::this_thread::sleep_for() for milliseconds and upward?

In a project I'm working on, I hope to replace a bouquet of platform-specific implementations all designed to yield for a specified number of milliseconds, give or take, using anything from Microsoft's Sleep() to naonsleep() to usleep , depending on what is available on a particular platform. Some of these come with notable limitations, such as not supporting delays of 1000 ms or more on certain platforms.

Does std::this_thread::sleep_for() suffer from such quirks, too?

Maximum sleep duration: std::this_thread::sleep_for() does not have any problem here. You can sleep as long as you like.

Precision: std::this_thread::sleep_for() has the same problems which all the other sleep functions you mention also have: Probably not! See update below!

Any sleep function will suffer the quirk that it is entirely up to the scheduler in your operating system when your task is actually resumed. Your sleep times will generally alias somewhat around the minimum time slice of your preemptive multitasking scheduler.

To make things worse, this time slice is not always constant on all OSes.

On Linux you usually have 10 ms time slices and sleeping less than 10 ms may result in sleeping 0 ms. Sleeping for 10 ms may result in sleeping for about 10 ms, or longer, potentially but not necessarily aligned to a time slice size.

In short: You cannot rely at all on any of the sleep functions, including std::this_thread::sleep_for() .

There is another class of sleep functions which does busy waiting. This is usually used when waiting for times which are significantly shorter than a scheduling time slice (say 2 us). But of course even this may be very inaccurate since your task may get preempted even on an almost idle system, and then you can add 10 ms to your 2 us sleep time.

On a multitasking operating system you have no chance: You cannot sleep precisely. The sleep functions have errors, and they even have systematic errors, so sleeping 100 times 10 ms may sleep anything between 0 and 2 seconds in practice.

If you have long-term time requirements the only chance you have is to constantly query the wall-clock time.

Update: Benchmarks on Linux and macOS:

At least on Linux and macOS std::this_thread::sleep_for() are pretty precise for millisecond resolution and so not suffer from all the artifacts I described above:

Linux benchmark: Each sleep is called repeatedly for a total duration of one second and the real average sleep time is given (eg averaging over 200 calls to sleep(5 ms)):

std::this_thread::sleep( 1 ms) slept really 1.13915 ms
std::this_thread::sleep( 2 ms) slept really 2.15215 ms
std::this_thread::sleep( 3 ms) slept really 3.14976 ms
std::this_thread::sleep( 4 ms) slept really 4.15059 ms
std::this_thread::sleep( 5 ms) slept really 5.15062 ms
std::this_thread::sleep( 6 ms) slept really 6.15008 ms
std::this_thread::sleep( 7 ms) slept really 7.14988 ms
std::this_thread::sleep( 8 ms) slept really 8.14979 ms
std::this_thread::sleep( 9 ms) slept really 9.15044 ms
std::this_thread::sleep(10 ms) slept really 10.1504 ms
std::this_thread::sleep(11 ms) slept really 11.1511 ms
std::this_thread::sleep(12 ms) slept really 12.1505 ms
std::this_thread::sleep(13 ms) slept really 13.1504 ms
std::this_thread::sleep(14 ms) slept really 14.1501 ms
std::this_thread::sleep(15 ms) slept really 15.1503 ms
std::this_thread::sleep(16 ms) slept really 16.1499 ms
std::this_thread::sleep(17 ms) slept really 17.1505 ms
std::this_thread::sleep(18 ms) slept really 18.1505 ms
std::this_thread::sleep(19 ms) slept really 19.1504 ms
std::this_thread::sleep(20 ms) slept really 20.1505 ms

Same for macOS:

std::this_thread::sleep( 1 ms) slept really 1.27451 ms
std::this_thread::sleep( 2 ms) slept really 2.45646 ms
std::this_thread::sleep( 3 ms) slept really 3.61991 ms
std::this_thread::sleep( 4 ms) slept really 4.77443 ms
std::this_thread::sleep( 5 ms) slept really 5.7994 ms
std::this_thread::sleep( 6 ms) slept really 7.03769 ms
std::this_thread::sleep( 7 ms) slept really 8.13089 ms
std::this_thread::sleep( 8 ms) slept really 9.13276 ms
std::this_thread::sleep( 9 ms) slept really 10.441 ms
std::this_thread::sleep(10 ms) slept really 11.5895 ms
std::this_thread::sleep(11 ms) slept really 12.77 ms
std::this_thread::sleep(12 ms) slept really 13.8207 ms
std::this_thread::sleep(13 ms) slept really 14.9366 ms
std::this_thread::sleep(14 ms) slept really 16.4569 ms
std::this_thread::sleep(15 ms) slept really 17.27 ms
std::this_thread::sleep(16 ms) slept really 18.2013 ms
std::this_thread::sleep(17 ms) slept really 19.6347 ms
std::this_thread::sleep(18 ms) slept really 20.7785 ms
std::this_thread::sleep(19 ms) slept really 22.9571 ms
std::this_thread::sleep(20 ms) slept really 23.2532 ms

Both runs are on an idle system. Interesting: On Linux the numbers get more precise on a loaded system (yes in a screen session). Scheduler artifacts! But small ones! :-)

This is for usleep() on Linux: Also pretty precise. I no longer believe what I wrote above:

usleep( 1 ms) slept really  1.148 ms
usleep( 2 ms) slept really  2.152 ms
usleep( 3 ms) slept really  3.151 ms
usleep( 4 ms) slept really  4.151 ms
usleep( 5 ms) slept really  5.149 ms
usleep( 6 ms) slept really  6.149 ms
usleep( 7 ms) slept really  7.149 ms
usleep( 8 ms) slept really  8.150 ms
usleep( 9 ms) slept really  9.150 ms
usleep(10 ms) slept really 10.150 ms
usleep(11 ms) slept really 11.149 ms
usleep(12 ms) slept really 12.149 ms
usleep(13 ms) slept really 13.150 ms
usleep(14 ms) slept really 14.150 ms
usleep(15 ms) slept really 15.149 ms
usleep(16 ms) slept really 16.149 ms
usleep(17 ms) slept really 17.150 ms
usleep(18 ms) slept really 18.150 ms
usleep(19 ms) slept really 19.149 ms
usleep(20 ms) slept really 20.149 ms

sleep_for supports longer delays, as it accepts any valid duration value. However, it does have limitations:

sleep_for generally uses std::chrono::steady_clock , and is thus subject to the limitations of that clock. The standard requires that clock to monotonically advance. But the resolution may vary from implementation to implementation.

Also,

This function may block for longer than sleep_duration due to scheduling or resource contention delays. sleep_for (cpp reference)

So sleep_for may sleep for longer than specified.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM