在 Windows 上,您可以为进程调用 SetProcessAffinityMask,为线程调用 SetThreadAffinityMask。 然而,Windows 似乎只公开 GetProcessAffinityMask,而不是针对进程的各个线程的类似 API。
我有一个多线程程序,它在运行时将各个线程绑定到处理器。 当我运行它时,我想(外部)查询哪些线程正在哪些处理器上运行,以确保它正常工作。 我编写了一个小型命令行实用程序来执行此操作。 但我似乎无法找到一种方法来查找单个线程绑定到哪个处理器或核心。
这显然是可能的;我在网上看到了有关 adplus 调试实用程序的描述,该实用程序能够显示类似 pstack 的输出以显示线程关联性。 Process Explorer 在多处理器计算机上显示“线程”选项卡,其中显示线程的“理想处理器”。
有谁知道如何查询这条信息吗?
SetThreadAffinityMask
来完成此操作。此函数返回传递的线程句柄的原始关联掩码。
因此...使用将亲和力设置为一个 CPU 的掩码执行一次调用,然后执行第二次调用以恢复原始掩码。
这里是完整的 C/C++ 源代码,包括错误检查:
DWORD GetThreadAffinityMask(HANDLE thread)
{
DWORD mask = 1;
DWORD old = 0;
// try every CPU one by one until one works or none are left
while(mask)
{
old = SetThreadAffinityMask(thread, mask);
if(old)
{ // this one worked
SetThreadAffinityMask(thread, old); // restore original
return old;
}
else
{
if(GetLastError() != ERROR_INVALID_PARAMETER)
return 0; // fatal error, might as well throw an exception
}
mask <<= 1;
}
return 0;
}
此代码一次探测一个 CPU,直到设置关联起作用(在这种情况下,我们现在知道原始掩码!)或直到初始
1
已移出 DWORD
。如果询问某个 CPU 不可用,则该功能会失败并显示 ERROR_INVALID_PARAMETER
,我们只需尝试下一个。通常第一个 CPU 就可以工作,所以效率相当高。
如果该函数因
ERROR_INVALID_PARAMETER
以外的任何原因失败,则意味着我们没有足够的句柄访问权限,或者系统存在一些实际问题,因为它无法满足我们的请求。因此在这种情况下继续下去是没有意义的。
调用 NtQueryInformationThread,使用 ThreadBasicInformation:
typedef struct _THREAD_BASIC_INFORMATION
{
NTSTATUS ExitStatus;
PTEB TebBaseAddress;
CLIENT_ID ClientId;
ULONG_PTR AffinityMask;
KPRIORITY Priority;
LONG BasePriority;
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
据我所知,没有记录的方法来获取线程亲和力。
在 Windows 7 及更高版本上,它们具有 GetThreadGroupAffinity
GROUP_AFFINITY affinity;
if (!GetThreadGroupAffinity(GetCurrentThread(), &affinity))
{
auto const code = GetLastError();
throw std::system_error(
std::error_code{ static_cast<int>(code), std::system_category()},
"GetThreadGroupAffinity");
}
return affinity.Mask
但是:
从 Windows 11 和 Windows Server 2022 开始,在具有超过 64 个处理器的系统上,默认情况下,进程和线程关联性跨越系统中的所有处理器、所有处理器组。 GetThreadGroupAffinity 函数检索线程主组上的组亲和力。
更快的方法是调用
GetCurrentProcessorNumber
(参见 msdn),它将返回调用此函数期间当前线程正在运行的处理器的编号。
c#代码:
/// <summary>
/// Retrieves the number of the processor the current thread was running on <para/>
/// during the call to this function.
/// </summary>
/// <returns>The function returns the current processor number.</returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int GetCurrentProcessorNumber();