Linux下获取当前线程CPU占用率需读取/proc/[tid]/stat第14、15字段(utime/stime),以syscall(SYS_gettid)获取tid,两次采样差值除以间隔时间并归一化,方得近似百分比。

Linux 下用 /proc/self/stat 读取当前线程的 CPU 时间
Linux 内核为每个线程在 /proc/[tid]/stat 中维护一份运行时统计,其中第 14 和 15 字段(utime 和 stime)分别表示用户态和内核态的时钟滴答数(CLK_TCK 单位)。注意:要查**当前线程**,得先获取 tid(不是 pid),再拼路径 —— 直接读 /proc/self/stat 拿到的是主线程(即主线程的 tid)的快照,不是整个进程的聚合值。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用
syscall(SYS_gettid)获取当前线程真实 tid(pthread_self()返回的是 pthread_t,不能直接当路径用) - 拼接路径:
/proc/+std::to_string(tid)+/stat,然后fopen读取并按空格分割字段(注意第 14、15 字段从 1 开始计数) - 需除以
sysconf(_SC_CLK_TCK)转为秒级时间;两次采样相减再除以间隔秒数,才能算出近似 CPU 使用率(%) - 该方法开销极低,适合高频采样(如每 100ms),但本身不提供“占用率”百分比,只是原始时间戳,必须自行差分计算
Windows 下调用 GetThreadTimes 获取线程 CPU 时间
Windows 没有类似 /proc 的文件接口,必须用 Win32 API。关键函数是 GetThreadTimes,它接受一个 HANDLE 并返回四个 FILETIME:创建时间、退出时间、内核时间、用户时间。对当前线程,可用 GetCurrentThread() 获取句柄(注意它返回伪句柄,需用 DuplicateHandle 才能在跨线程场景复用,但单次查询无需复制)。
实操建议:
立即学习“C++免费学习笔记(深入)”;
-
FILETIME是 100ns 单位,需转成毫秒或秒:先转为ULARGE_INTEGER,再除以 10000 得毫秒 - 和 Linux 一样,仅一次调用无意义;必须保存上次的
kernelTime和userTime,两次差值除以采样间隔才接近“CPU 占用率” - 若线程已退出,
GetThreadTimes可能失败(返回 0),需检查GetLastError()是否为ERROR_INVALID_HANDLE - 该 API 不支持获取其他进程的线程数据(权限不足时会失败),且无法区分超线程/逻辑核归属
为什么不能用 clock() 或 std::chrono::steady_clock?
这两个是墙内常见误用点:clock() 在 POSIX 中定义为“进程 CPU 时间”,但实际行为高度依赖实现——glibc 下它等价于 times(),返回的是整个进程所有线程的累计时间,**不是当前线程**;而 std::chrono::steady_clock 是单调时钟,只反映挂钟流逝,完全不体现 CPU 占用。
常见错误现象:
- 多线程程序中,
clock()值增长远快于 wall-clock,且各线程调用结果几乎相同(因为累加了所有线程) - 用
steady_clock差值除以 wall-clock 差值,得到的永远是 ~100%,哪怕线程大部分时间在sleep或等待锁 - 某些平台(如 macOS)的
clock()甚至只返回 wall-clock,彻底失效
跨平台封装要注意的三个坑
如果写通用库,别直接封装成 “get_current_thread_cpu_usage()”,因为语义模糊:用户想要的是瞬时率?平均率?是否含系统调用时间?这些必须显式暴露参数。
容易踩的坑:
- Linux 下不同线程的
/proc/[tid]/stat需要各自读取,不能复用主线程路径;Windows 下GetCurrentThread()返回的伪句柄不能跨CloseHandle复用 - 两次采样间隔太短(如 utime/stime 可能无变化,导致计算出 0%;间隔太长(如 >1s),又失去“当前”的意义
- 没有考虑线程生命周期:目标线程可能在两次采样间已退出(Linux 下
/proc/[tid]目录消失,Windows 下句柄失效),必须做存在性检查,否则读文件或调用 API 会失败
真正可靠的“当前线程 CPU 占用”本质是一个差分估算值,依赖稳定采样节奏和上下文一致性;任何想绕过两次采样的方案,基本都在猜。



















