关于 GetThreadTimes
我在一篇博文中 《对老赵写的简单性能计数器的修改 》 提到用 GetThreadTimes 这个Win32 API 来获取线程实际运行时间。今天我又深入研究了一下,发现这个API 返回的时间并不准确。
首先我们先看一下 GetThreadTimes 的实现原理:
在 kernel32.dll 内部 GetThreadTimes 首先调用 NtQueryInformationThread 获取线程TCB信息,然后从TCB 中获取线程的内核态计数和用户态计数。然而这个计数值并不是实时增加的,操作系统只是在时间中断(10/15ms一次,单处理器通常是10ms,多处理器 15ms)发生时增加当前线程的计数。这里就出现一个问题,如果时间中断发生时,被统计线程恰好不在活动状态,即不是当前线程,那么这个线程就不会增加计 数。所以只有线程长时间连续占用CPU,或者每次都正好连续占用一个完整的时间片(10/15ms一次),才能确保这个线程的统计结果是准确的。但实际情 况是,线程不可能连续占用CPU(否则其他程序就别玩了,如果你一定要这样我也没办法),也不可能每次都恰巧占用完整的时间片(线程工作过程中往往会需要 Sleep,或者阻塞等待资源或者被高优先级线程打断)。所以显而易见,这个计数是不精确的。
下面给出我写的一个示例代码。
这个代码在我机器上运行,如果将Thread.Sleep(1); 这行注释掉,显示耗时是140毫秒,但如果不注释这行,显示耗时是0毫秒。
原因就是我们将140毫秒的时间强行分成了1000个很小的时间,每次线程只消耗大约0.14 毫秒的CPU时间,而在这1秒左右的时间(约 1000*1ms+140ms)
时间里,共发生1140 / 15 = 74 次时间中断(我的机器是双核的),每次中断时,主线程正好在运行的概率不高,如果这74次中断中没有一次击中当前线程,
则当前线程的计数增长始终为0。
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/4fd96b3cf02f4c7b5c8964ac8167f7af.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/f0cd6c7f9e7ae96feae062cb48f670f0.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3112b7b6526db5bc83e275260ae60525.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/587e34b10dcf5efbc0859b53470a2db3.gif)