HOWTO · C++
在 C++ 中添加定时延迟
本教程将为你提供有关在 C++ 程序中添加定时延迟的简要指南。
本页内容
C++ 程序可以通过平台 API 或标准库的线程等待功能暂停当前线程。选择时应考虑代码的可移植性,以及等待目标是一个时长还是某个时钟时间点:POSIX 系统上的 sleep() 使用整秒,usleep() 使用微秒,而 C++11 的 sleep_for() 和 sleep_until() 分别接受时长和时间点。这些调用会挂起调用它们的线程,但无法保证线程会在某个精确时刻恢复执行。
选择一种 C++ 延迟 API
在普通应用程序中,需要可移植的 C++11 相对等待时,使用 sleep_for();需要等到某个时钟的截止时间时,使用 sleep_until()。如果代码已经依赖 POSIX,sleep() 和 usleep() 也很实用,但它们不是可移植的 C++ 功能。Windows 提供了自己的 Sleep() API,参数单位为毫秒。
延迟会阻塞调用它的线程。如果用户界面、服务器或工作线程池需要保持响应,应让专用线程等待,或使用框架提供的异步计时器,避免阻塞主线程或事件循环线程。
使用 sleep() 实现整秒的平台专用延迟
C++ 并未定义可移植的 sleep() 函数。在 POSIX 系统上,应通过 <unistd.h> 声明它,并调用 sleep(秒数)。如果信号中断等待,它会返回尚未等待的整秒数;如果请求的时间已经过去,则返回零。在 Windows 上,应包含 <Windows.h> 并调用 Sleep(毫秒数);其单位是毫秒,因此延迟一秒应调用 Sleep(1000)。POSIX 和 Windows 的调用名称、头文件及单位都不同。Windows 头文件也可参阅Windows.h 指南;目前没有对应的简体中文目标,因此保留原链接。
例如,在包含 <unistd.h> 后,可在 POSIX 系统上调用 sleep(2);。在 Windows 上,则应在包含 <Windows.h> 后使用 Sleep(2000);。应根据目标平台选择相应分支;仅有 POSIX 声明并不会让 Windows 支持 sleep()。
使用 usleep() 实现 POSIX 微秒延迟
在 POSIX 系统上,usleep() 由 <unistd.h> 声明,接受以微秒为单位的时长。例如,usleep(250000); 会请求暂停四分之一秒。它是 POSIX 接口,而非可移植的 C++ 功能;某些平台或功能设置可能不提供它,因此跨平台 C++ 代码应使用 sleep_for()。等待完成时,该函数返回 0;被中断或因其他原因失败时返回 -1。如果调用方需要处理等待中断的情况,应检查返回值。由于操作系统会自行调度线程,实际经过的时间可能长于请求的间隔。
使用 sleep_for() 实现相对延迟
包含 <thread> 和 <chrono>,即可等待一段相对时长。sleep_for() 会让当前线程至少暂停指定的时长,但实际等待时间会受到线程调度和资源竞争的影响。下面这个完整的 C++11 程序会暂停 200 毫秒,然后打印确认信息。运行此示例:
本节中的代码以及显示的标签或输出仍为英文。
#include <chrono>
#include <iostream>
#include <thread>
int main() {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
std::cout << "Wait complete\n";
}
Wait complete
选择能清楚表达预期单位的时长类型,例如 std::chrono::seconds(2) 或 std::chrono::milliseconds(250)。等待只影响调用它的线程;调度延迟可能使实际经过的时间长于传给函数的时长。
使用 sleep_until() 等待某个时间点
sleep_until() 同样来自 <thread>,但它接受时钟时间点,而非时长。将时长加到时钟的 now() 值上,即可得到未来的截止时间。例如:std::this_thread::sleep_until(std::chrono::steady_clock::now() + std::chrono::seconds(3));。这会等待至 steady_clock 上的该时间点;此时钟适合测量时间间隔,不会像墙上时钟那样被调整。若截止时间已经过去,则不会额外等待。该函数不返回值,而且线程可能因调度原因在截止时间之后才恢复执行。
测量经过时间的截止时间应优先使用 steady_clock,这样系统时钟校正不会改变目标时间。只有在目标确实是墙上时钟时间时才使用 system_clock,并应考虑等待期间该时钟可能发生变化。与 sleep_for() 一样,这个函数会阻塞调用它的线程。
选择合适的延迟方式并考虑调度
对于可移植的 C++11 代码,需求是“等待这么长时间”时使用 sleep_for(),需求是“等到这个截止时间”时使用 sleep_until()。只有在适合依赖 POSIX 特定行为时才使用 sleep() 或 usleep();Windows 专用代码则使用 Windows 的 Sleep()。应明确标示单位:sleep() 接受秒,usleep() 接受微秒,Windows 的 Sleep() 接受毫秒,而 <chrono> 使用带名称的类型表示时长。
这些 API 都不是精密计时器。请求的时长只是等待目标的下限,并不保证线程会在精确时刻恢复;调度、资源竞争和中断行为都会影响程序继续执行的时间。若短暂等待期间不能阻塞线程,应使用平台或框架提供的异步计时器。了解这些差异,才能根据程序的可移植性需求和计时方式选择合适的延迟 API。