C++ timer utilities | sleep_for, sleep_until, and chrono literals

C++ timer utilities | sleep_for, sleep_until, and chrono literals

이 글의 핵심

How to delay threads with chrono, avoid system_clock sleep_until pitfalls, fixed-rate loops, and when to use asynchronous timers instead of blocking sleep.

What are timer utilities?

Delays and timing using <thread> and <chrono>.

#include <thread>
#include <chrono>
using namespace std::chrono_literals;

std::this_thread::sleep_for(1s);

auto wakeup = std::chrono::system_clock::now() + 5s;
std::this_thread::sleep_until(wakeup);

sleep_for

using namespace std::chrono_literals;

std::this_thread::sleep_for(100ms);
std::this_thread::sleep_for(2s);
std::this_thread::sleep_for(1min);

Practical examples

Example 1: Periodic loop

using namespace std::chrono_literals;

void periodicTask() {
    while (true) {
        std::cout << "tick" << std::endl;
        std::this_thread::sleep_for(1s);
    }
}

Example 2: sleep_until

using namespace std::chrono;
using namespace std::chrono_literals;

void scheduledTask() {
    auto now = system_clock::now();
    auto nextRun = now + 5s;
    
    std::cout << "Scheduled in 5s" << std::endl;
    std::this_thread::sleep_until(nextRun);
    
    std::cout << "run" << std::endl;
}

Example 3: Precision-oriented timer (busy/yield hybrid)

using namespace std::chrono;
using namespace std::chrono_literals;

class PrecisionTimer {
    steady_clock::time_point start;
    
public:
    PrecisionTimer() : start(steady_clock::now()) {}
    
    void waitFor(milliseconds duration) {
        auto target = start + duration;
        
        while (steady_clock::now() < target) {
            std::this_thread::yield();
        }
    }
    
    auto elapsed() const {
        return duration_cast<microseconds>(
            steady_clock::now() - start
        );
    }
};

int main() {
    PrecisionTimer timer;
    timer.waitFor(100ms);
    
    std::cout << "Elapsed: " << timer.elapsed().count() << " μs" << std::endl;
}

Example 4: Timeout polling

using namespace std::chrono;
using namespace std::chrono_literals;

bool waitForCondition(milliseconds timeout) {
    auto deadline = steady_clock::now() + timeout;
    
    while (steady_clock::now() < deadline) {
        if (checkCondition()) {
            return true;
        }
        
        std::this_thread::sleep_for(10ms);
    }
    
    return false;
}

int main() {
    if (waitForCondition(5s)) {
        std::cout << "condition met" << std::endl;
    } else {
        std::cout << "timeout" << std::endl;
    }
}

yield

std::this_thread::yield();

while (!ready) {
    std::this_thread::yield();
}

Common pitfalls

Pitfall 1: Accuracy

using namespace std::chrono_literals;

auto start = std::chrono::steady_clock::now();
std::this_thread::sleep_for(100ms);
auto end = std::chrono::steady_clock::now();

auto actual = duration_cast<milliseconds>(end - start);
std::cout << "Actual: " << actual.count() << " ms" << std::endl;

Pitfall 2: Clock choice for sleep_until

// ❌ system_clock — user can change wall time
auto wakeup = std::chrono::system_clock::now() + 5s;
std::this_thread::sleep_until(wakeup);

// ✅ steady_clock for relative deadlines
auto wakeup = std::chrono::steady_clock::now() + 5s;
std::this_thread::sleep_until(wakeup);

Pitfall 3: Busy-wait

while (!ready) { }

while (!ready) {
    std::this_thread::yield();
}

while (!ready) {
    std::this_thread::sleep_for(1ms);
}

Pitfall 4: Very short sleep

std::this_thread::sleep_for(std::chrono::microseconds(1));

Measuring elapsed time

using namespace std::chrono;

auto start = steady_clock::now();
std::this_thread::sleep_for(100ms);
auto end = steady_clock::now();
auto elapsed = duration_cast<milliseconds>(end - start);

std::cout << "Elapsed: " << elapsed.count() << " ms" << std::endl;

Common patterns

  1. Relative delay: sleep_for(d) — at least d (often longer).
  2. Wake at time_point: sleep_until(tp) — use steady_clock for monotonic deadlines.
  3. Polling: short sleep_for in a loop to avoid burning CPU.
  4. Deadline style: deadline = steady_clock::now() + timeout — same idea as steady_clock.
using namespace std::chrono_literals;

void fixed_rate_loop() {
    auto next = std::chrono::steady_clock::now();
    const auto interval = 1s;
    for (int i = 0; i < 10; ++i) {
        next += interval;
        do_tick();
        std::this_thread::sleep_until(next);
    }
}

Asio timers (sketch)

For async servers, prefer asio::steady_timer / asio::system_timer on the io_context instead of blocking worker threads.

  • steady_timer: monotonic—timeouts, retries, heartbeats.
  • system_timer: wall-clock—jobs at a local time of day.

See network guide — post/dispatch/defer for executors and strands.

Thread sleep blocks that thread; Asio timers can fire without blocking other handlers on the same io_context.

Advanced: periodic work

  • Fixed rate: naive sleep_for(interval) drifts by work duration; next += interval; sleep_until(next) tracks wall-clock spacing better.
  • Backoff: exponential retry with cap and jitter for network clients.
  • Shutdown: use std::stop_token (C++20) or an atomic flag to exit long loops cleanly.

Advanced: retry with deadline

#include <chrono>
#include <thread>

template <class Fn>
bool retry_with_deadline(Fn&& fn, std::chrono::steady_clock::time_point deadline,
                         std::chrono::milliseconds backoff) {
    while (std::chrono::steady_clock::now() < deadline) {
        if (fn()) return true;
        std::this_thread::sleep_for(backoff);
    }
    return false;
}

Performance notes

  • Sub-millisecond sleep often resolves to tens–hundreds of microseconds on many OSes.
  • Tight poll + sleep still costs scheduler wakeups—tune the minimum poll interval.
  • yield-only loops can still burn a core—prefer sleep or proper blocking primitives.
  • Many threads each sleeping still adds scheduler load—consider one reactor + timer queue.

FAQ

Q1: sleep_for?

A: Block for (at least) a duration.

Q2: sleep_until?

A: Block until a time_point.

Q3: yield?

A: Hint to reschedule; lighter than sleep, heavier than blocking on a condvar.

Q4: Accuracy?

A: Best-effort minimum; actual wait is often longer.

Q5: Very short sleeps?

A: OS scheduler granularity limits usefulness.

Q6: Learning resources?

A: C++ Concurrency in Action, C++ Primer, cppreference — sleep_for.


  • C++ Chrono guide
  • C++ Chrono
  • C++ steady_clock

Practical tips

Debugging

  • Fix warnings first.
  • Reproduce minimally.

Performance

  • Profile before optimizing.

Code review

  • Follow team conventions.

Production checklist

Before coding

  • Right tool?
  • Maintainable?
  • Meets timing requirements?

While coding

  • Warnings cleared?
  • Edge cases covered?

At review

  • Intent clear?
  • Tests sufficient?

Keywords

C++, timer, sleep, thread, chrono


  • C++ Chrono guide |
  • C++ Calendar & Timezone |
  • C++ Chrono literals |
  • C++ Chrono |
  • C++ duration |