0%

解决C++中sleep对系统时间的依赖

最近遇到一个业务上修改Windows系统时间导致程序“卡住”的问题,经定位是std::this_thread::sleep_for函数阻塞线程时间长于预期。例如将系统时间往后调一小时,原本阻塞一秒的std::this_thread::sleep_for居然阻塞了一小时之久。查阅相关资料发现是std::this_thread::sleep_for的实现依赖于系统时间,如果系统时间修改则其行为不符合预期。

问了ChatGPT解决方案,它的回答是:

在C++中,sleep函数是用于暂停程序执行指定时间的。当系统时间被修改时,和sleep相关的函数例如std::this_thread::sleep_forstd::this_thread::sleep_util可能会表现出异常行为,因为它通常依赖于系统时钟。为了规避这个问题,你可以使用std::chrono库中的高精度时钟(std::chrono::steady_clock)来实现延时操作,因为它不受系统时间修改的影响。

以下是使用std::this_thread::sleep_forstd::chrono::steady_clock实现稳定延时的示例代码:

#include <iostream>
#include <thread>
#include <chrono>

void delay(std::chrono::milliseconds duration) {
auto start = std::chrono::steady_clock::now();
while (std::chrono::steady_clock::now() - start < duration) {
std::this_thread::yield(); // 让出CPU时间片,降低资源占用
}
}

int main() {
std::cout << "程序开始执行" << std::endl;

auto delay_duration = std::chrono::milliseconds(5000); // 设定延时时间,例如5000毫秒
delay(delay_duration);

std::cout << "延时结束,程序继续执行" << std::endl;

return 0;
}

此代码片段中的delay函数使用std::chrono::steady_clock来确保延时不受系统时间修改的影响。这种方法降低了程序对系统时间变化的敏感性,使得程序更稳定。

std::chrono::steady_clockstd::chrono::system_clock是C++标准库中的组件,它们的详细信息可以在C++标准库的官方参考文档中找到:

  1. std::chrono::steady_clockhttps://en.cppreference.com/w/cpp/chrono/steady_clock

  2. std::chrono::system_clockhttps://en.cppreference.com/w/cpp/chrono/system_clock

请注意,这些文档是针对C++标准库的,因此不会涉及到特定操作系统或编译器的实现细节。如果您需要了解特定平台上的实现,请查阅相应平台的文档。

C++官方文档将std::chrono::steady_clock定义为一个单调(monotonic)时钟,它不会随着修改时间而改变,而是一直稳定增加,可以理解为开机起到当前的时间。std::chrono::system_clock则是系统时钟,随着用户修改时间而修改。对于可能涉及用户修改时间的场景官方建议使用std::chrono::steady_clock,所以从直觉上看std::chrono::sleep_forstd::chrono::sleep_util应当基于std::chrono::steady_clock实现。

实际情况是:通过分析VS2017上MSVC的STL代码,可以发现的确相关实现用到了std::chrono::system_clock::now()。经测试VS2017的环境上std::chrono::sleep_for在向前/后拨动时钟都会卡住,std::chrono::sleep_util则是往前拨动时钟卡住。而VS2020的std::chrono::sleep_forstd::chrono::sleep_util往后拨动时钟不再卡住,而往前拨动时钟依然卡住。看到相关论坛对该BUG的上报:https://developercommunity.visualstudio.com/t/this-threadsleep-for-hangs-when-system-clock-moved/169569。其中VS2020上改BUG的上报:https://github.com/microsoft/STL/issues/718,似乎并未得到解决。

所以要解决不同编译器版本下Windows下sleep的系统时钟拨动问题,最好还是用上文说的std::chrono::steady_clock::now()配合std::this_thread::yield()实现自己的sleep函数,或者用Windows API:Sleep()

Disqus评论区没有正常加载,请使用科学上网