如何查询Windows系统中指定线程运行的CPU核心?
获取指定线程实际运行的CPU核心方案
问题分析
你当前用SetThreadAffinityMask获取的是线程亲和掩码,它表示线程允许运行的CPU核心集合,而非实际正在运行的核心。默认情况下,线程的亲和掩码是全1(即所有核心都允许),所以你看到的FFFFFFF是正常现象,这个方法无法满足你获取实际运行核心的需求。
可行解决方案
在Windows用户态下,可通过未公开的NtQueryInformationThreadAPI获取线程当前运行的CPU核心ID,以下是具体实现步骤和代码:
1. 核心API说明
NtQueryInformationThread是ntdll.dll中的未公开函数,通过指定ThreadCurrentCpuId(信息类值为0x18),可直接获取线程当前所在的CPU核心ID。
2. 代码实现
(1)声明函数与常量
#include <windows.h> #include <tlhelp32.h> #include <stdio.h> // 定义NTSTATUS类型和成功判断宏 typedef LONG NTSTATUS; #define NT_SUCCESS(status) ((status) >= 0) // 线程信息类枚举 typedef enum _THREAD_INFORMATION_CLASS { ThreadCurrentCpuId = 0x18 // 指定获取当前CPU ID } THREAD_INFORMATION_CLASS; // NtQueryInformationThread函数原型 typedef NTSTATUS(NTAPI* PNtQueryInformationThread)( HANDLE ThreadHandle, THREAD_INFORMATION_CLASS ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength );
(2)获取线程当前CPU核心ID的函数
BOOL GetThreadCurrentCpuId(HANDLE hThread, DWORD* pCpuId) { if (!hThread || !pCpuId) return FALSE; // 加载ntdll.dll并获取函数地址 HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll"); if (!hNtdll) return FALSE; PNtQueryInformationThread NtQueryInformationThread = (PNtQueryInformationThread)GetProcAddress(hNtdll, "NtQueryInformationThread"); if (!NtQueryInformationThread) return FALSE; DWORD cpuId = 0; ULONG returnLength = 0; NTSTATUS status = NtQueryInformationThread( hThread, ThreadCurrentCpuId, &cpuId, sizeof(cpuId), &returnLength ); if (NT_SUCCESS(status)) { *pCpuId = cpuId; return TRUE; } return FALSE; }
(3)枚举所有线程并输出对应信息
void EnumAllThreadsWithCpuId() { // 创建线程快照 HANDLE hThreadSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hThreadSnapshot == INVALID_HANDLE_VALUE) { printf("Failed to create thread snapshot\n"); return; } THREADENTRY32 te32; te32.dwSize = sizeof(THREADENTRY32); if (!Thread32First(hThreadSnapshot, &te32)) { printf("Failed to get first thread\n"); CloseHandle(hThreadSnapshot); return; } do { // 打开线程句柄,需要THREAD_QUERY_INFORMATION权限 HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID); if (hThread) { DWORD cpuId = 0; if (GetThreadCurrentCpuId(hThread, &cpuId)) { printf("PID: %-6lu TID: %-6lu 当前CPU核心: %lu\n", te32.th32OwnerProcessID, te32.th32ThreadID, cpuId); } CloseHandle(hThread); } } while (Thread32Next(hThreadSnapshot, &te32)); CloseHandle(hThreadSnapshot); } int main() { // 需管理员权限运行,否则无法访问部分系统线程 EnumAllThreadsWithCpuId(); system("pause"); return 0; }
3. 注意事项
- 权限要求:程序需以管理员身份运行,否则无法打开部分进程的线程句柄。
- 结果误差:线程可能在获取CPU ID后立即切换核心,建议多次运行程序采样,统计线程在特定核心的出现频率,锁定持续占用目标核心的线程。
- 版本兼容性:
ThreadCurrentCpuId信息类在Windows 7及以上版本均支持,若需兼容更早版本,可考虑使用WMI查询Win32_Thread类的CurrentProcessorNumber属性,但性能会稍差。
4. 筛选异常线程的思路
由于你的目标是找到持续占用单个核心的线程,可:
- 多次运行上述程序,记录每个线程的CPU核心分布
- 统计每个线程在同一核心出现的次数,找到高频出现的线程
- 结合
GetThreadTimesAPI计算线程的运行时间占比,进一步确认是否为持续占用的异常线程
内容的提问来源于stack exchange,提问作者chacham15
相关产品推荐
相关产品推荐

