Linux下获取线程CPU时间应优先使用clock_gettime(CLOCK_THREAD_CPUTIME_ID),它返回纳秒级总CPU时间(用户态+内核态),轻量且标准;拆分用户/内核时间需读取/proc/self/stat中第14、15字段并结合sysconf(_SC_CLK_TCK)换算。

Linux下用clock_gettime获取线程CPU时间
在Linux上,clock_gettime配合CLOCK_THREAD_CPUTIME_ID能直接拿到当前线程的总CPU时间(用户态+内核态之和),这是最轻量、最标准的方式。它不依赖glibc版本(2.17+即可),也不需要读取/proc伪文件。
注意:这个时钟返回的是该线程自调度开始以来消耗的CPU时间,不是挂钟时间,且只在该线程运行时才累加。
-
CLOCK_THREAD_CPUTIME_ID是POSIX标准定义的,但仅Linux内核支持;FreeBSD等系统不兼容 - 返回值单位是纳秒,需除以
1000000000.0转为秒 - 必须链接
-lrt(尤其在较老的编译环境中)
#include <time.h>
#include <iostream>
struct timespec ts;
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) {
double total_sec = ts.tv_sec + ts.tv_nsec / 1000000000.0;
std::cout << "Thread CPU time: " << total_sec << "s\n";
}
拆分用户态与内核态时间要用/proc/self/stat
Linux内核不提供直接分离用户/内核时间的POSIX接口。/proc/self/stat是唯一可靠且开销可控的途径——它暴露了线程级的utime(用户态jiffies)和stime(内核态jiffies),通过系统HZ可换算成秒。
关键点:/proc/self/stat中第14字段是utime,第15字段是stime,单位是内核jiffies(非固定频率,需查sysconf(_SC_CLK_TCK))。
立即学习“C++免费学习笔记(深入)”;
- 不能硬编码
HZ=100——现代内核常为250或1000,必须调用sysconf(_SC_CLK_TCK) - 读取
/proc/self/stat是线程安全的,但需注意字段位置可能因内核版本微调(建议用空格分割后按索引取,而非正则匹配) - 该文件每次读取都触发轻量内核查询,频繁调用(如每毫秒)会有可观开销
#include <unistd.h>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
double get_utime_sec() {
long clk_tck = sysconf(_SC_CLK_TCK);
std::ifstream f("/proc/self/stat");
std::string line;
std::getline(f, line);
std::istringstream iss(line);
std::vector<std::string> fields;
std::string field;
while (iss >> field) fields.push_back(field);
if (fields.size() >= 15) {
long utime = std::stol(fields[13]); // 14th field, 0-indexed
return static_cast<double>(utime) / clk_tck;
}
return 0.0;
}
Windows平台只能用GetThreadTimes,且无跨线程精确性保证
Windows没有线程级的用户/内核时间分离API,GetThreadTimes是唯一选择,但它返回的是“该线程句柄所代表线程”的时间,且文档明确指出:对已退出线程调用结果未定义,对其他线程调用可能返回过期或不一致值。
更麻烦的是:它返回的FILETIME需手动转为秒,且无法区分当前线程和其他线程——你得确保传入的是当前线程句柄(GetCurrentThread()),而非任意句柄。
-
GetThreadTimes要求线程处于活动状态;若线程刚结束,结果可能为空或错误 - 返回的
CreateTime、ExitTime在当前线程上调用时无意义,只应关注KernelTime和UserTime - FILETIME是100纳秒单位,转秒要除以
10000000.0
#ifdef _WIN32
#include <windows.h>
#include <iostream>
void print_thread_times() {
FILETIME creation, exit, kernel, user;
if (GetThreadTimes(GetCurrentThread(), &creation, &exit, &kernel, &user)) {
ULARGE_INTEGER u, k;
u.LowPart = user.dwLowDateTime; u.HighPart = user.dwHighDateTime;
k.LowPart = kernel.dwLowDateTime; k.HighPart = kernel.dwHighDateTime;
std::cout << "User: " << u.QuadPart / 10000000.0 << "s, "
<< "Kernel: " << k.QuadPart / 10000000.0 << "s\n";
}
}
#endif
别试图用getrusage替代——它只对进程有效
getrusage(RUSAGE_SELF, ...)返回的是整个进程的累计用户/内核时间,不是当前线程的。即使你在多线程程序里调用,它也包含所有线程的贡献,且无法剥离单个线程的占比。
常见误用场景:想监控某worker线程性能,却在该线程里调用getrusage(RUSAGE_SELF)——结果是整个进程的历史累加值,毫无参考价值。
-
RUSAGE_THREAD在glibc中存在但未被内核真正支持(始终返回ENOSYS或0),不要依赖 - 某些旧代码用
times(&tms),它同样只作用于进程,且精度低(tick级)、已被clock_gettime取代 - 如果真需要线程粒度的资源统计,Linux cgroup v2 +
cpu.stat是更重但更准确的方案,但需root权限和额外配置
clock_gettime(CLOCK_THREAD_CPUTIME_ID)拿总量,再配合/proc/self/stat拆分;Windows上只能接受GetThreadTimes的局限性。跨平台封装时,务必把“线程时间”和“进程时间”逻辑彻底隔离,否则很容易在高并发场景下得出误导性数据。


















