最近遇到一个业务上修改Windows系统时间导致程序“卡住”的问题,经定位是std::this_thread::sleep_for
函数阻塞线程时间长于预期。例如将系统时间往后调一小时,原本阻塞一秒的std::this_thread::sleep_for
居然阻塞了一小时之久。查阅相关资料发现是std::this_thread::sleep_for
的实现依赖于系统时间,如果系统时间修改则其行为不符合预期。
问了ChatGPT解决方案,它的回答是:
在C++中,sleep函数是用于暂停程序执行指定时间的。当系统时间被修改时,和sleep相关的函数例如
std::this_thread::sleep_for
和std::this_thread::sleep_util
可能会表现出异常行为,因为它通常依赖于系统时钟。为了规避这个问题,你可以使用std::chrono
库中的高精度时钟(std::chrono::steady_clock
)来实现延时操作,因为它不受系统时间修改的影响。以下是使用
std::this_thread::sleep_for
和std::chrono::steady_clock
实现稳定延时的示例代码:
|
此代码片段中的delay函数使用std::chrono::steady_clock
来确保延时不受系统时间修改的影响。这种方法降低了程序对系统时间变化的敏感性,使得程序更稳定。
std::chrono::steady_clock
和std::chrono::system_clock
是C++标准库中的组件,它们的详细信息可以在C++标准库的官方参考文档中找到:
std::chrono::steady_clock
:https://en.cppreference.com/w/cpp/chrono/steady_clockstd::chrono::system_clock
:https://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_for
和std::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_for
和std::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()