前面介绍过将进程或线程绑定到特定CPU核心的方法,但单纯的绑定有时还不够。因为虽然调度器不会再把你的线程调度到其他核心,但其他线程仍然可以被调度到你绑定的核心上,与你争夺计算资源。
那么,有没有办法能让某个核心“专属于”你的线程,彻底杜绝干扰呢?
在Linux上,这相对简单,可以通过内核启动参数 isolcpus 来隔离指定CPU。被隔离的核心默认不会用于调度普通进程,你可以通过 taskset 或程序中的 sched_setaffinity 等函数,手动将你的进程/线程绑定上去,从而实现近乎独占的效果(内核自身的少量后台任务仍会运行)。
实际上,Windows系统也提供了类似的功能,只不过其配置方式是通过修改注册表来实现的。具体操作步骤如下:
- 打开注册表编辑器 (
regedit)
- 导航到以下路径:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\kernel
- 在该路径下,检查是否存在名为
ReservedCpuSets 的注册表项。如果不存在,则需要新建一个,类型为 二进制值 (REG_BINARY)。
- 设置
ReservedCpuSets 的值。这个二进制值的每一位对应一个逻辑处理器(CPU核心),从低位(第0位)开始。例如,在一台8核电脑上,如果想隔离最后两个核心(CPU6和CPU7),需要设置的二进制数值为 0b11000000,对应的十六进制数为 0xC0。在编辑时,通常输入十六进制值即可。

- 重启计算机使配置生效。
重启后,打开任务管理器查看CPU利用率,你会发现被隔离的核心(本例中的CPU6和CPU7)利用率几乎为0,与其他核心形成鲜明对比。这证明调度器已不再主动将线程调度到这两个核心上。

为了进一步验证,可以运行10个死循环程序。观察任务管理器,即使其他6个核心的利用率已被打满,被隔离的两个核心依然“稳如泰山”,不受影响。

那么,在程序中如何识别哪些CPU已被隔离呢?这就要用到Windows的CPU Sets相关API,特别是 GetSystemCpuSetInformation 函数。它能获取系统中所有CPU Set的信息,其结构体 SYSTEM_CPU_SET_INFORMATION 中的 Allocated 成员就表明了该CPU是否已被分配(隔离)。运行以下程序即可查看:
#include<windows.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char *argv[])
{
DWORD size = 0;
DWORD count = 0;
PSYSTEM_CPU_SET_INFORMATION info = NULL;
// 第一次调用,获取所需缓冲区大小
BOOL success = GetSystemCpuSetInformation(NULL, 0, &size, GetCurrentProcess(), 0);
if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
printf("Failed to get buffer size, error: %lu\n", GetLastError());
return 1;
}
// 分配缓冲区
info = (PSYSTEM_CPU_SET_INFORMATION)malloc(size);
if (info == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// 第二次调用,获取实际信息
success = GetSystemCpuSetInformation(info, size, &size, GetCurrentProcess(), 0);
if (!success) {
printf("Failed to get CPU set information, error: %lu\n", GetLastError());
free(info);
return 1;
}
// 计算 CPU 集数量
DWORD cpuSetCount = size / sizeof(SYSTEM_CPU_SET_INFORMATION);
printf("%-10s %-12s %-10s %-15s %-15s %-15s\n",
"CPU Index", "CpuSet ID", "Group", "Core Index", "Logical Index", "Allocated");
printf("============================================================================\n");
// 遍历并打印信息(每个 CPU 一行)
DWORD validCpuCount = 0;
for (DWORD i = 0; i < cpuSetCount; ++i) {
if (info[i].Type == CpuSetInformation) {
validCpuCount++;
printf("%-10lu %-12lu %-10hu %-15hu %-15hu %-15hu\n",
validCpuCount - 1, // CPU编号(从0开始)
info[i].CpuSet.Id,
info[i].CpuSet.Group,
info[i].CpuSet.CoreIndex,
info[i].CpuSet.LogicalProcessorIndex,
info[i].CpuSet.Allocated
);
}
}
// 计算并显示 CPU 总数
printf("============================================================================\n");
printf("Total logical processors: %lu\n", validCpuCount);
free(info);
return 0;
}
程序运行结果如下,可以看到只有CPU6和CPU7对应的 Allocated 值为1,证实它们已被隔离。

核心已被隔离,接下来就是如何将程序运行在上面。实际上,之前介绍的线程绑定方法依然有效。例如,可以通过任务管理器,右键点击目标进程,选择“设置相关性”,然后勾选已被隔离的核心(如CPU7)。


设置后再次观察,你会发现CPU7的利用率立刻飙升到100%,而同样被隔离但未绑定任何进程的CPU6,其利用率依然为0。

除了使用任务管理器,更常用的方式是在程序中直接调用Windows API,例如 SetThreadAffinityMask。下面的代码演示了如何将当前线程绑定到已被隔离的CPU6上:
#include<windows.h>
#include<stdio.h>
int main(int argc, char* argv[])
{
HANDLE thread = GetCurrentThread();
// 设置线程亲和性掩码,将线程绑定到第6个CPU核心(CPU6,从0开始计数)
DWORD_PTR affinityMask = 1 << 6;
DWORD_PTR prevAffinityMask = SetThreadAffinityMask(thread, affinityMask);
if (prevAffinityMask == 0)
{
DWORD dwError = GetLastError();
printf("SetThreadAffinityMask failed! Error code: %lu\n", dwError);
return 1;
}
printf("Thread affinity set successfully!\n");
printf("Previous affinity mask: 0x%I64X\n", prevAffinityMask);
printf("Current affinity mask: 0x%I64X (CPU 6)\n", affinityMask);
while (1) {}
return 0;
}
该程序将自己绑定到CPU6后执行死循环,通过任务管理器可以看到CPU6的利用率随即变为100%。

需要注意:之前介绍的Windows CPU Sets相关API(如 SetProcessDefaultCpuSets)并不能将线程绑定到已被 ReservedCpuSets 隔离的核心上。即使调用成功,也不会生效,线程仍然可能被调度到其他非隔离核心。
总结
要实现线程对CPU核心的独占,需要两步:
- 隔离目标CPU核心:阻止系统调度器将普通线程分配上去。
- Linux: 通过内核启动参数
isolcpus 实现。
- Windows: 通过修改注册表
HKEY_LOCAL_MACHINE\SYSTEM...\kernel\ReservedCpuSets 实现。
- 将目标线程绑定到已隔离的核心:
- Linux: 使用
taskset 命令或 sched_setaffinity 等系统调用。
- Windows: 使用任务管理器或
SetThreadAffinityMask 等API。
这种方法适用于对性能抖动极其敏感、需要实现软实时效果,或希望特定关键任务完全不受其他进程干扰的场景。如果你正在从事高性能计算、低延迟交易或实时音视频处理等领域的多线程开发,不妨尝试一下。
本文介绍的通过 ReservedCpuSets 注册表隔离CPU的方法,其思路来源于 GitHub 项目:https://github.com/valleyofdoom/ReservedCpuSets,在此表示感谢。