跳转到内容
新建笔记

C++ 线程等待:Sleep、sleep 与 sleep_for

先分清 Sleep、sleep 和 sleep_for

跳转到“先分清 Sleep、sleep 和 sleep_for”

原记录在 move_kinova.cpp 中遇到 C3861“找不到标识符”,标题写 Sleep,正文却混用了 sleep。C++ 标识符区分大小写;Windows API 的名称是 Sleep。包含 Windows SDK 头文件可以提供它的声明,但不会把小写 sleep 变成同一个函数。

接口环境与头文件参数含义
Sleep(20)Windows API,通常包含 <windows.h>当前线程等待约 20 毫秒,实际调度有误差
sleep(1)POSIX,<unistd.h>以秒为单位,可被信号打断并返回未睡完的秒数
std::this_thread::sleep_for(std::chrono::milliseconds(20))C++11 起,<thread> 与 <chrono>使用明确的时长类型阻塞当前线程

POSIX sleep 的中断与返回值语义见 POSIX sleep。

找不到声明时先检查名称、头文件、当前平台和 SDK。若源码已编译成目标文件,后来报告未解析的外部符号,那是链接问题,需要另查库依赖。Sleep 的 Windows 文档列出 Kernel32.lib;普通 Windows 工具链通常已默认处理这个基础依赖。Microsoft Sleep 文档

可移植等待与 Windows 分支

跳转到“可移植等待与 Windows 分支”

下面保存为 sleep_demo.cpp。标准等待使用 steady_clock 记录经过时间;Windows 分支另外调用一次 Sleep,其他系统不会编译这段代码。

#include <cassert>
#include <chrono>
#include <iostream>
#include <thread>
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif
int main()
{
using Clock = std::chrono::steady_clock;
const auto requested = std::chrono::milliseconds(20);
const auto begin = Clock::now();
std::this_thread::sleep_for(requested);
const auto elapsed = Clock::now() - begin;
assert(elapsed >= requested);
std::cout << "sleep_for elapsed us: "
<< std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count()
<< '\n';
#ifdef _WIN32
Sleep(20);
std::cout << "Windows Sleep returned\n";
#endif
}

GCC/MinGW 可使用 g++ -std=c++17 sleep_demo.cpp -o sleep_demo -pthread;Windows PowerShell 运行 ./sleep_demo.exe,Linux shell 运行 ./sleep_demo。本例在 Windows MinGW 和 Linux GCC 上分别验证;这不等同于复现原项目的 MSVC 诊断。

这里的断言只检查标准相对等待没有提前结束,没有设定“必须精确在 20 毫秒返回”的上限。操作系统计时粒度、调度和其他工作会推迟线程重新执行;Sleep(0) 主要用于让出剩余时间片,也不表示等待 0 毫秒后立即独占 CPU。不要用上述接口实现严格实时的脉冲或采样时序。C++ 线程等待要求

两个线程之间需要等待数据时,用互斥量、条件变量或合适的同步对象表达条件。反复“睡一会再看”不能自动建立数据访问的同步关系。

在 GUI 主线程调用这些函数会阻塞事件处理,窗口可能不能重绘或响应输入。Qt 中需要稍后执行界面逻辑时可使用定时器和事件循环;真正耗时的任务需要设计后台执行和结果交付。原 C3861 问题应通过正确声明解决,不能据此把阻塞等待当作所有延迟工作的通用方案。