The simplest and most robust solution is to avoid using any form of sleep(), usleep(), or nanosleep() altogether. In well-designed, portable, and efficient code, explicit sleeping is rarely necessary, especially for precise timing.
However, if you do choose to use sleep functions, use sleep() only for relatively long delays (typically 10-20 milliseconds or more). These are suitable for coarse timing, throttling, or waiting for external events.
If you find yourself needing sleep intervals shorter than 1-10 ms, it's usually a sign that something is fundamentally wrong with your algorithm or design. Code relying on sub-millisecond sleeps will almost certainly face serious portability, performance, and timing issues, especially on general-purpose operating systems. You may not realize it yet, but this approach leads to unpredictable behavior and difficult-to-diagnose bugs down the road.
In certain scenarios, particularly in real-time systems, delays shorter than 10 ms may indeed be necessary. This is typically implemented using spin locks, where a thread actively waits in a tight loop, continuously checking a condition. While this approach is inefficient in terms of CPU usage, it can be justified in specific high-performance or low-latency contexts.
That said, such use cases are quite specialized. If you're working in this domain, it's likely you already have a solid understanding of thread scheduling and low-level system behavior. In that case, the trade-offs and implications of using spin waits are something you're already well aware of.
However, for most general-purpose applications, you should avoid using delays shorter than 10 milliseconds. In typical scenarios, such short sleeps offer no real benefit and are more likely to introduce subtle bugs, timing issues, and performance problems, especially if you’re not fully aware of when and why such delays might be justified. Simply put, it’s best to steer clear of them unless you have a compelling and well-understood reason.
Well-designed code typically avoids the use of explicit delays altogether. Instead, it relies on event-driven architectures, callbacks (such as lambdas), and other asynchronous mechanisms to handle timing and coordination efficiently and responsively. This approach leads to more maintainable, scalable, and portable code.
In short, just avoid using any kind of sleep altogether. If you find yourself reaching for it, take it as a red flag - it’s usually a strong indication that something in your design or logic needs to be re-evaluated.