Windows普通与挂起线程创建到获权耗时对比结果混乱,是否合理?
测试结果说明
结论
你观测到的无规律测试结果完全符合预期,且创建挂起线程后立即恢复的操作,确实不会对该耗时统计产生可观测的显著影响。
原因解释
- Windows系统的线程调度本身存在天然的不确定性:线程完成初始化进入就绪队列后,需要等待操作系统调度器分配CPU时间片才能执行,这个等待时间取决于当前系统整体负载、CPU核心占用情况、系统中其他线程的优先级、调度器的时间片分配策略,本身就会存在较大的随机波动。你仅执行10次测试的样本量过小,观测到无规律的波动是正常现象,只有将样本量提升到数千甚至数万次,剔除极值后取平均值,才能得到具备统计意义的稳定结果。
- 你的计时逻辑本身包含了额外的操作耗时:你将计时起点设置在
CreateThread调用之前,统计的是从调用CreateThread前到线程入口函数开始执行的总耗时,包含了内核执行线程创建、初始化线程内核对象、分配线程栈等操作的时间,这部分操作本身也存在微小波动,进一步放大了结果的随机性。 - 挂起后立即恢复的操作耗时可以忽略:传入
CREATE_SUSPENDED参数创建线程时,内核会完成所有线程创建的初始化工作,仅将线程的挂起计数设为1,不将其加入就绪队列。调用ResumeThread仅会将挂起计数减到0,再把线程加入就绪队列,和直接创建非挂起线程的最终状态完全一致。多出来的一次ResumeThread系统调用的耗时仅为几十纳秒级别,和线程调度的微秒级随机波动相比完全可以忽略,因此你无法观测到两种场景的显著差异。
测试优化建议
如果要得到更稳定的测试结果,可以做以下调整:
- 增大测试样本量,至少执行数千次测试,剔除最高、最低的10%极值后取平均,消除随机波动的影响
- 将计时起点调整到
CreateThread返回之后,仅统计线程创建完成后到开始执行的调度耗时,排除线程创建本身的耗时干扰 - 测试时关闭无关的后台进程,固定测试线程的优先级为较高等级,降低系统其他进程对调度结果的干扰
测试代码
#include <iostream> #include <windows.h> using namespace std; DWORDLONG freq; DWORD WINAPI startThread(LPVOID lpParam) { DWORDLONG c_beg = *((DWORDLONG*)lpParam); DWORDLONG c_end; QueryPerformanceCounter((LARGE_INTEGER*)&c_end); cout << (double(c_end - c_beg)) / (freq) << endl; return 0; } int main() { DWORDLONG c_beg; HANDLE iThread, sThread; QueryPerformanceFrequency((LARGE_INTEGER*)&freq); cout << "instant" << endl; for (int i = 0; i < 10; i++) { QueryPerformanceCounter((LARGE_INTEGER*)&c_beg); iThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)startThread, (LPVOID*)&c_beg, 0, NULL); WaitForSingleObject(iThread, INFINITE); } cout << "suspended" << endl; for (int i = 0; i < 10; i++) { QueryPerformanceCounter((LARGE_INTEGER*)&c_beg); sThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)startThread, (LPVOID*)&c_beg, CREATE_SUSPENDED, NULL); ResumeThread(sThread); WaitForSingleObject(sThread, INFINITE); } CloseHandle(iThread); CloseHandle(sThread); return 0; }
测试结果截图

内容的提问来源于stack exchange,提问作者Kurz Weber
相关产品推荐
相关产品推荐

