IMPORTANT UPDATE
Note: since this question is specifically about timers, its important to note there is a bug in gcc that if you are using std::condition_variable::wait_for (or wait_util) it uses the system clock even if you pass it a std::chrono::steady_clock time point. This means the timer is not monotonic - i.e. if you change the system time forward by a day then your timer may not trigger for a day + your timeout - if you change the time backwards your timer may trigger immediately.
See: condition_variable workaround for wait_until with system time change
The fix for this bug went into gcc v10+
END
I have the following code (hand-copied in):
// Simple stop watch class basically takes "now" as the start time and // returns the diff when asked for. class stop_watch {...} // global var std::thread timer_thread; void start_timer(int timeout_ms) { timer_thread = std::thread([timeout_ms, this](){ stop_watch sw; while (sw.get_elapsed_time() < timeout_ms) { // Here is the sleep to stop from hammering a CPU std::this_thread::sleep_for(std::chrono::milliseconds(10)); } // Do timeout things here... std::cout << "timed out!" << std::endl; }) }
I did not want to get too bogged down in the detail of the class I writing so this is a very cut-down version. The full class calls a function call-back and has a variable to cancel the timer etc...
I just wanted to focus on the "sleep" part. Can I implement something like this without a sleep or is there a better way to do it? - or is sleep perfectly good? - I was of the opinion that sleeps are generally a sign of bad design (I have read that a few places)... but I can't think of a way to implement a timer without one :(
Additional Note: The timer should have the requirement to be able to be stopped/woken at any time. Just adding that for clarity because it appears to affect what kind of solution to go for. In my original code (not this snippet) I used an atomic bool flag that can break out of the loop.
The function sleep gives a simple way to make the program wait for a short interval. If your program doesn't use signals (except to terminate), then you can expect sleep to wait reliably throughout the specified interval.
The timer() function in C++ returns the updated time as an object of the “time_t” type. The header file where this timer() function is defined is “ctime”.
Answer: The header for sleep is “unistd. h” for LINUX/UNIX Operating system and “Windows. h” for the Windows Operating system.
C++11 provides us with std::condition_variable
. In your timer you can wait until your condition has been met:
// Somewhere else, e.g. in a header: std::mutex mutex; bool condition_to_be_met{false}; std::condition_variable cv; // In your timer: // ... std::unique_lock<std::mutex> lock{mutex}; if(!cv.wait_for(lock, std::chrono::milliseconds{timeout_ms}, [this]{return condition_to_be_met;})) std::cout << "timed out!" << std::endl;
You can find more information here: https://en.cppreference.com/w/cpp/thread/condition_variable
To signal that the condition has been met do this in another thread:
{ std::lock_guard<std::mutex> lock{mutex}; // Same instance as above! condition_to_be_met = true; } cv.notify_one();
While your code will "work", it is sub-optimal for the intended purpose as timer.
There exists std::this_thread::sleep_until
which, depending on the implementation, possibly only just calls sleep_for
after doing some math anyway, but which might use a proper timer, which is vastly superior in terms of precision and reliability.
Generally, sleeping is not the best, most reliable, and most accurate thing to do, but sometimes, if just waiting for some approximate time is what's intended, it can be "good enough".
In any case, repeatedly sleeping for small amounts as in your example is a bad idea. This will burn a lot of CPU on needlessly rescheduling and waking threads, and on some systems (Windows in particular, though Windows 10 isn't so bad in that respect any more) it may add a considerable amount of jitter and uncertainity. Note that different Windows versions round to the scheduler's granularity differently, so in addition to generally being not overly precise, you do not even have consistent behavior. Rounding is pretty much "who cares" for a single large wait, but it is a serious problem for a series of small waits.
Unless the ability to abort the timer prematurely is a necessity (but in that case, there are better ways of implementing that, too!), you should sleep exactly once, never more, for the full duration. For correctness you should then check that you indeed got the time you expected because some systems (POSIX, notably) may under-sleep.
Over-sleeping is a different problem if you need it right, because even if you check and detect that case correctly, once it has happened there's nothing you can do about it (time has already passed, and never comes back). But alas, that's just a fundamental weakness of sleeping, not much you can do. Luckily, most people can shrug this problem off, most of the time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With