windows下设置线程亲和性(支持大于64核)

设置线程亲和性,通俗的说法就是将线程绑定到cpu上某一个或多个核上,此处的核是指逻辑核心,非物理核心。

物理核心与逻辑核心的关系,如果开启超线程,一般逻辑核心数=物理核心数*2。

一、SetThreadAffinityMask

微软帮助:

https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadaffinitymask

函数定义如下:

DWORD_PTR SetThreadAffinityMask(HANDLE hThread,DWORD_PTR dwThreadAffinityMask)

参数含义:

hThread - 线程句柄。可以通过GetCurrentThread()获取当前线程的句柄。
dwThreadAffinityMask - 亲和性掩码。长度共8个字节,每一位代表线程绑定的一个cpu核。

比如,需要将线程绑定到

第0个核上:则mask=0×01

第1个核上:则mask=0×02

第2个核上:则mask=0×04 (注意不是0×03)

第3个核上:则mask=0×08,以此类推。

若需要绑定到多个核,比如

第0、1个:mask=0×03

第1、2个:mask=0×06,以此类推。

我们使用qt编写测试代码

Thread1.h如下:

#ifndef THREAD1_H
#define THREAD1_H

#include <QThread>

class Thread1 : public QThread
{
    Q_OBJECT

protected:
    virtual void run() override;
};

#endif // THREAD1_H

Thread1.cpp如下:

#include "Thread1.h"
#include <Windows.h>

void Thread1::run()
{
    // 获取线程id
    quint64 threadId = (quint64)QThread::currentThreadId();

    // 线程id=>线程句柄
    HANDLE handle = OpenThread(THREAD_ALL_ACCESS, false, threadId);

    // 设置线程亲和性
    SetThreadAffinityMask(handle, 0x02);

    for (;;)
    {
    }
}

运行效果:

在这里插入图片描述

可以看到线程跑在第1个核上。

注意:

该函数第2个参数mask,只有8个字节大小,即8*8=64位,最多只能代表64个核。而某些服务器cpu核心数可能超过64,比如128核。

所以,在小于64核的系统上,可以使用此函数进行线程亲和性设置。
大于64核时,我们接下来看另外一个函数。

二、SetThreadGroupAffinity

微软帮助:

https://docs.microsoft.com/en-us/windows/win32/api/processtopologyapi/nf-processtopologyapi-setthreadgroupaffinity

函数定义如下:

BOOL SetThreadGroupAffinity(
  HANDLE               hThread,
  const GROUP_AFFINITY *GroupAffinity,
  PGROUP_AFFINITY      PreviousGroupAffinity
);

组亲和性结构如下:

typedef struct _GROUP_AFFINITY {
  KAFFINITY Mask;
  WORD      Group;
  WORD      Reserved[3];
} GROUP_AFFINITY, *PGROUP_AFFINITY;

参数含义:

hThread - 线程句柄。可以通过GetCurrentThread()获取当前线程的句柄。
GroupAffinity - 处理器组亲和性结构。Mask表示处理器组中亲和性掩码;Group表示处理器组编号,从0开始;Reserved表示保留,一般为0。

关于处理器组的概念,可以参考:

《win下处理器组概念》

阅读之后,对win下处理器分组方式有了了解。另外,由此函数中Group参数,可知,线程只能被绑定到一个处理器组内,而不能绑定多个处理器组,Mask就表示Group组内的一个或多个核。

如128核系统上,处理器组如下所示:

在这里插入图片描述

若线程需要绑定到处理器组0,则Group=0,

第0个核(Core0)上:则mask=0×01

第1个核(Core1)上:则mask=0×02

第2个核(Core2)上:则mask=0×04 (注意不是0×03)

第3个核(Core3)上:则mask=0×08,以此类推,与第一节中相同。

若线程需要绑定到处理器组1,则Group=1,

第0个核(Core64)上:则mask=0×01

第1个核(Core65)上:则mask=0×02

第2个核(Core66)上:则mask=0×04 (注意不是0×03)

第3个核(Core67)上:则mask=0×08,以此类推,与第一节中相同。

若需要绑定同个组内的多个核,与第一节中相同。

有时我们不关心处理器组编号,会使用拉通编号来表示处理器编号,如0-127核,而不是组0下0-63核,组1下64-127核。

比如需要将线程绑定到第64核,则需要先判断第64核属于哪个组,在组内排第几个。所以需要介绍2个函数,如下:

GetActiveProcessorGroupCount(),获取处理器组数量
GetActiveProcessorCount(WORD GroupNumber),获取某个处理器组下的处理器数量

由此2个函数可以编写一个由处理器编号获取所属组号的函数,如下:

int getGroupIndex(int processorIndex)
{
    int count = processorIndex + 1;
    WORD groupCount = GetActiveProcessorGroupCount();
    for (WORD i = 0; i < groupCount; i++)
    {
        count = count - GetActiveProcessorCount(i);
        if (count <= 0)
        {
            return i;
        }
    }
    return -1; // processorIndex is invalid
}

我们使用qt编写测试代码

Thread2.h如下:

#ifndef THREAD2_H
#define THREAD2_H

#include <QThread>

class Thread2 : public QThread
{
    Q_OBJECT

protected:
    virtual void run() override;
};

#endif // THREAD2_H

Thread2.cpp如下:

#include "Thread2.h"
#include <Windows.h>

void Thread2::run()
{
    // 获取线程id
    quint64 threadId = (quint64)QThread::currentThreadId();

    // 线程id=>线程句柄
    HANDLE handle = OpenThread(THREAD_ALL_ACCESS, false, threadId);

    // 处理器组亲和性结构
    GROUP_AFFINITY group_affinity;
    group_affinity.Reserved[0] = 0;
    group_affinity.Reserved[1] = 0;
    group_affinity.Reserved[2] = 0;
    group_affinity.Mask = 0x04;
    group_affinity.Group = 0x01;

    // 设置线程亲和性
    SetThreadGroupAffinity(handle, &group_affinity, nullptr);

    for (;;)
    {
    }
}

运行效果:

在这里插入图片描述

可以看到线程跑在第66个核上。

三、总结

SetThreadAffinityMask函数,只能用于逻辑核心数<=64的系统。

SetThreadGroupAffinity函数,因为增加了处理器组编号参数,所以既可以用于<=64核系统,也可以用于>64核系统。

需要注意的是,线程只能与单个处理器组下面的逻辑处理器进行绑定,不能跨处理器组绑定,即单个线程只能在一个处理器组范围内,如线程1可以与处理器组0下1、2、3、4号处理器绑定,而不能与处理器组0下1号处理器,处理器组1下2号处理器同时进行绑定。进程与线程类似也是不能跨处理器组的,但是由于进程下可以有多个线程,故可以通过设置不同的线程绑定到不同的处理器组上,间接实现进程跨处理器组绑定。



若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!

同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。

本文涉及工程代码,公众号回复:09SetThreadAffinityWin,即可下载。

在这里插入图片描述

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百里杨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值