可以,GetThreadTimes通过lpKernelTime参数直接返回线程累计内核态执行时间,单位为100纳秒,需句柄具THREAD_QUERY_INFORMATION权限且输出指针非空。

GetThreadTimes 能否直接获取内核态时间?
可以,但必须注意:Windows 的 GetThreadTimes 返回的是 FILETIME(100 纳秒为单位的 64 位整数),其中 lpKernelTime 参数就是该线程在内核模式下执行所消耗的时间总和。它不区分“当前瞬时”值,而是累计值——从线程创建开始到调用时刻为止的全部内核态 CPU 时间。
这个值不是实时采样,也不反映当前调度周期内的活跃状态;它只在内核实际执行线程代码时才增长(比如系统调用、中断处理、驱动代码等)。用户态代码运行时,该值不变。
调用 GetThreadTimes 前必须做的三件事
- 必须传入有效的线程句柄,且该句柄需具有
THREAD_QUERY_INFORMATION 访问权限。默认创建的线程句柄通常有,但如果用 CreateThread 并显式设置了 0 作为 dwCreationFlags,或通过 OpenThread 获取句柄,则很可能没权限,调用会失败并返回 ERROR_ACCESS_DENIED
- 所有四个输出参数(
lpCreationTime, lpExitTime, lpKernelTime, lpUserTime)都必须是非空指针,哪怕你只关心 lpKernelTime
- 调用前建议先调用
GetLastError() 清零错误码,否则失败时可能误读上次残留的错误信息
THREAD_QUERY_INFORMATION 访问权限。默认创建的线程句柄通常有,但如果用 CreateThread 并显式设置了 0 作为 dwCreationFlags,或通过 OpenThread 获取句柄,则很可能没权限,调用会失败并返回 ERROR_ACCESS_DENIED
lpCreationTime, lpExitTime, lpKernelTime, lpUserTime)都必须是非空指针,哪怕你只关心 lpKernelTime
GetLastError() 清零错误码,否则失败时可能误读上次残留的错误信息典型失败场景:跨进程获取其他线程句柄但未提升权限或未启用调试特权(SeDebugPrivilege);或在 UWP/沙盒环境里调用,这类环境默认禁止访问线程时间信息。
FILETIME 转毫秒或 double 秒的常见转换陷阱
FILETIME 是 64 位整数,低 32 位为 dwLowDateTime,高 32 位为 dwHighDateTime。直接强制类型转换(如 (int64_t)ft)会出错,因为它是小端布局的联合体结构,不是纯数值。
立即学习“C++免费学习笔记(深入)”;
正确做法是用 ULARGE_INTEGER 中间转换:
FILETIME ftKernel; GetThreadTimes(hThread, nullptr, nullptr, &ftKernel, nullptr); ULARGE_INTEGER uli; uli.LowPart = ftKernel.dwLowDateTime; uli.HighPart = ftKernel.dwHighDateTime; int64_t kernel100ns = uli.QuadPart; // 单位:100 纳秒 double kernelMs = kernel100ns / 10000.0; // 转毫秒
别用 FileTimeToSystemTime 或 FileTimeToLocalFileTime——它们用于日期时间,不适用于线程时间戳。
为什么两次调用差值有时为 0?
- 线程在两次调用之间完全没进入内核态(比如纯计算循环 + 缓存命中,无系统调用、无页错误、无上下文切换开销)
- 时间增量小于 15.625ms(Windows 默认时钟精度),而
GetThreadTimes 底层依赖内核统计粒度,短时间低负载下无法分辨变化
- 多核系统中,若线程频繁迁移 CPU,某些旧版 Windows 内核存在统计延迟或丢失(尤其在 Win7 及更早版本)
GetThreadTimes 底层依赖内核统计粒度,短时间低负载下无法分辨变化实测建议:至少间隔 100ms 以上再采样;对关键路径做多次差值取平均;若需更高精度,应考虑 ETW(Event Tracing for Windows)配合 Thread/ThreadTime 事件,而非轮询 GetThreadTimes
内核态时间本身不包含线程等待时间(比如 Sleep、WaitForSingleObject),它只会计入真正占用 CPU 的内核执行时间。这点容易误解,也最难验证——因为任务管理器里的“内核时间”列其实是 UI 汇总后做了平滑处理的,和你用 GetThreadTimes 拿到的原始值未必一致。


















