【问题标题】:Query thread (not process) processor affinity?查询线程(不是进程)处理器亲和性?
【发布时间】:2015-04-30 23:58:45
【问题描述】:

在 Windows 上,您可以为进程调用 SetProcessAffinityMask,为线程调用 SetThreadAffinityMask。但是,Windows 似乎只公开了 GetProcessAffinityMask,而不是用于进程的各个线程的类似 API。

我有一个多线程程序,可以在运行时将各个线程绑定到处理器。当我运行它时,我想(从外部)查询哪些线程在哪些处理器上运行,以确保它正常工作。我编写了一个小型命令行实用程序来执行此操作。但我似乎找不到找到单个线程绑定到哪个处理器或内核的方法。

这显然是可能的;我在网上看到过关于 adplus 调试实用程序的描述,它能够显示类似 pstack 的输出以显示线程亲和性。 Process Explorer 在多处理器机器上显示线程选项卡,显示线程的“理想处理器”。

有人知道如何查询这条信息吗?

【问题讨论】:

    标签: windows multithreading winapi


    【解决方案1】:

    您可以通过两次调用 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 以外的任何内容而失败,则意味着我们没有足够的句柄访问权限,或者系统由于无法满足我们的请求而出现了一些实际问题。因此,在这种情况下继续下去是没有意义的。

    【讨论】:

    • Damon,谢谢!--效果很好。一个后续问题:如果这被集成到一个任务列表样式的实用程序中,这如何通过潜在地对每个正在运行的进程上的每个线程的亲和掩码进行位抨击来影响进程性能?
    • “正常”情况是线程对所有 CPU 都具有亲和力,包括第一个 CPU。事实并非如此,这确实很不寻常。因此,您通常可以预期对 SetThreadAffinityMask 的调用不超过 2 次。此外,我希望 SetThreadAffinityMask 的开销相当低,因为它或多或少可以通过 TEB 直接设置信息,甚至不需要上下文切换。所以性能应该有点类似于例如GetLastError 函数。我不希望通过调用它一百次会产生任何明显的影响(尽管只有分析才能确定!)。
    • 仍然...您真的需要同时获取系统上所有线程的亲和性吗?如果您特别要求,大多数任务列表样式的程序一次只查询 一个 线程的亲和性。此外,如果它确实是一个问题,您仍然可以查询查询一个的工作线程中所有线程的亲和性,并调用 SwitchToThread 或 Sleep(0),然后查询下一个......并向 GUI 发送消息每次更新的线程。这将很快填满整个列表并且影响为零。
    【解决方案2】:

    调用 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;
    

    AFAIK 没有记录在案的方法来获取线程关联性。

    【讨论】:

      【解决方案3】:

      更快的方法是调用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();
      

      【讨论】:

        猜你喜欢
        • 2015-03-13
        • 2017-12-06
        • 1970-01-01
        • 1970-01-01
        • 2020-01-28
        • 2011-01-15
        • 2012-11-07
        • 1970-01-01
        相关资源
        最近更新 更多