You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何查询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. 筛选异常线程的思路

由于你的目标是找到持续占用单个核心的线程,可:

  1. 多次运行上述程序,记录每个线程的CPU核心分布
  2. 统计每个线程在同一核心出现的次数,找到高频出现的线程
  3. 结合GetThreadTimesAPI计算线程的运行时间占比,进一步确认是否为持续占用的异常线程

内容的提问来源于stack exchange,提问作者chacham15

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 00:13:16