精华内容
下载资源
问答
  • 线程亲和性
    2020-07-26 17:18:57

    linux下线程亲和性主要就是通过pthread_setaffinity_np()函数来完成。

    首先将线程需要绑定的core编号,添加到一个集合中,然后调用此函数就ok。

    测试代码如下:

    #define _GUN_SOURCE
    #include <unistd.h>
    #include <pthread.h>
    #include <sched.h>
    #include <stdio.h>
    
    void *testfunc(void *arg)
    {
    	cpu_set_t mask;
        CPU_ZERO(&mask);
        for (int i = 0; i < 4; i++) 
    	{ /*将Core0、1、2、3添加到集合中*/
            CPU_SET(i, &mask);
        }
    
        /* 设置cpu 亲和性(affinity)*/
        if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) 
    	{
            fprintf(stderr, "set thread affinity failed\n");
        }
    
    	for(;;)
    	{
    		
    	}
    	
        pthread_exit(NULL);
    }
    
    int main(int argc, char *argv[])
    {
        pthread_t tid;
        if (pthread_create(&tid, NULL, (void *)testfunc, NULL) != 0) 
    	{
            fprintf(stderr, "thread create failed\n");
            return -1;
        }
    
        pthread_join(tid, NULL);
        return 0;
    }
    

    编译:

    gcc main.c -o main.out -D_GNU_SOURCE -lpthread
    

    运行效果:

    在这里插入图片描述

    可以看到线程跑在核3上。我们设置了多个核,所以根据内部状态,可能会跑在核0-3中一个上。

    参考链接:

    linux进程、线程与cpu的亲和性(affinity)



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

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

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

    在这里插入图片描述

    更多相关内容
  • 线程亲和性

    2021-05-15 13:41:04
    亲和性(affinity)意味着进程并不会在处理器之间频繁迁移,而硬亲和性(affinity)则意味着进程需要在您指定的处理器上运行。本文介绍了当前的亲和性(affinity)机制,解释为什么和如何使用亲和性(affinity),并给出了...

    解 Linux? 2.6 调度器如何处理 CPU 亲和性(affinity)可以帮助您更好地设计用户空间的应用程序。软亲和性(affinity) 意味着进程并不会在处理器之间频繁迁移,而 硬亲和性(affinity) 则意味着进程需要在您指定的处理器上运行。本文介绍了当前的亲和性(affinity)机制,解释为什么和如何使用亲和性(affinity),并给出了几个样例代码来显示如何使用这种功能。

    简单地说,CPU 亲和性(affinity) 就是进程要在某个给定的 CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性。Linux 内核进程调度器天生就具有被称为 软 CPU 亲和性(affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。

    2.6 版本的 Linux 内核还包含了一种机制,它让开发人员可以编程实现 硬 CPU 亲和性(affinity)。这意味着应用程序可以显式地指定进程在哪个(或哪些)处理器上运行。

    在 Linux 内核中,所有的进程都有一个相关的数据结构,称为 task_struct。这个结构非常重要,原因有很多;其中与 亲和性(affinity)相关度最高的是 cpus_allowed 位掩码。这个位掩码由 n 位组成,与系统中的 n 个逻辑处理器一一对应。 具有 4 个物理 CPU 的系统可以有 4 位。如果这些 CPU 都启用了超线程,那么这个系统就有一个 8 位的位掩码。

    如果为给定的进程设置了给定的位,那么这个进程就可以在相关的 CPU 上运行。因此,如果一个进程可以在任何 CPU 上运行,并且能够根据需要在处理器之间进行迁移,那么位掩码就全是 1。实际上,这就是 Linux 中进程的缺省状态。

    Linux 内核 API 提供了一些方法,让用户可以修改位掩码或查看当前的位掩码:sched_set_affinity() (用来修改位掩码)

    sched_get_affinity() (用来查看当前的位掩码)

    注意,cpu_affinity 会被传递给子线程,因此应该适当地调用 sched_set_affinity。36491_2710049_10.gif

    36491_2710049_2.gif

    通常 Linux 内核都可以很好地对进程进行调度,在应该运行的地方运行进程(这就是说,在可用的处理器上运行并获得很好的整体性能)。内核包含了一些用来检测 CPU 之间任务负载迁移的算法,可以启用进程迁移来降低繁忙的处理器的压力。

    一般情况下,在应用程序中只需使用缺省的调度器行为。然而,您可能会希望修改这些缺省行为以实现性能的优化。让我们来看一下使用硬亲和性(affinity) 的 3 个原因。

    基于大量计算的情形通常出现在科学和理论计算中,但是通用领域的计算也可能出现这种情况。一个常见的标志是您发现自己的应用程序要在多处理器的机器上花费大量的计算时间。

    测试复杂软件是我们对内核的亲和性(affinity)技术感兴趣的另外一个原因。考虑一个需要进行线性可伸缩性测试的应用程序。有些产品声明可以在 使用更多硬件 时执行得更好。

    我们不用购买多台机器(为每种处理器配置都购买一台机器),而是可以:购买一台多处理器的机器

    不断增加分配的处理器

    测量每秒的事务数

    评估结果的可伸缩性

    如果应用程序随着 CPU 的增加可以线性地伸缩,那么每秒事务数和 CPU 个数之间应该会是线性的关系(例如斜线图 —— 请参阅下一节的内容)。这样建模可以确定应用程序是否可以有效地使用底层硬件。36491_2710049_2.gifAmdahl 法则

    Amdahl 法则是有关使用并行处理器来解决问题相对于只使用一个串行处理器来解决问题的加速比的法则。加速比(Speedup) 等于串行执行(只使用一个处理器)的时间除以程序并行执行(使用多个处理器)的时间:T(1)

    S = ------

    T(j)

    其中 T(j) 是在使用 j 个处理器执行程序时所花费的时间。

    Amdahl 法则说明这种加速比在现实中可能并不会发生,但是可以非常接近于该值。对于通常情况来说,我们可以推论出每个程序都有一些串行的组件。随着问题集不断变大,串行组件最终会在优化解决方案时间方面达到一个上限。

    Amdahl 法则在希望保持高 CPU 缓存命中率时尤其重要。如果一个给定的进程迁移到其他地方去了,那么它就失去了利用 CPU 缓存的优势。实际上,如果正在使用的 CPU 需要为自己缓存一些特殊的数据,那么所有其他 CPU 都会使这些数据在自己的缓存中失效。

    因此,如果有多个线程都需要相同的数据,那么将这些线程绑定到一个特定的 CPU 上是非常有意义的,这样就确保它们可以访问相同的缓存数据(或者至少可以提高缓存的命中率)。否则,这些线程可能会在不同的 CPU 上执行,这样会频繁地使其他缓存项失效。

    我们对 CPU 亲和性(affinity)感兴趣的最后一个原因是实时(对时间敏感的)进程。例如,您可能会希望使用硬亲和性(affinity)来指定一个 8 路主机上的某个处理器,而同时允许其他 7 个处理器处理所有普通的系统调度。这种做法确保长时间运行、对时间敏感的应用程序可以得到运行,同时可以允许其他应用程序独占其余的计算资源。

    下面的样例应用程序显示了这是如何工作的。36491_2710049_10.gif

    36491_2710049_2.gif

    现在让我们来设计一个程序,它可以让 Linux 系统非常繁忙。可以使用前面介绍的系统调用和另外一些用来说明系统中有多少处理器的 API 来构建这个应用程序。实际上,我们的目标是编写这样一个程序:它可以让系统中的每个处理器都繁忙几秒钟。可以从后面的“下载”一节中 下载样例程序。清单 1. 让处理器繁忙/* This method will create threads, then bind each to its own cpu. */

    bool do_cpu_stress(int numthreads)

    {

    int ret = TRUE;

    int created_thread = 0;

    /* We need a thread for each cpu we have... */

    while ( created_thread < numthreads - 1 )

    {

    int mypid = fork();

    if (mypid == 0) /* Child process */

    {

    printf("\tCreating Child Thread: #%i\n", created_thread);

    break;

    }

    else /* Only parent executes this */

    {

    /* Continue looping until we spawned enough threads! */ ;

    created_thread++;

    }

    }

    /* NOTE: All threads execute code from here down! */

    正如您可以看到的一样,这段代码只是通过 fork 调用简单地创建一组线程。每个线程都执行这个方法中后面的代码。现在我们让每个线程都将亲和性(affinity)设置为自己的 CPU。清单 2. 为每个线程设置 CPU 亲和性(affinity)cpu_set_t mask;

    /* CPU_ZERO initializes all the bits in the mask to zero. */

    CPU_ZERO( &mask );

    /* CPU_SET sets only the bit corresponding to cpu. */

    CPU_SET( created_thread, &mask );

    /* sched_setaffinity returns 0 in success */

    if( sched_setaffinity( 0, sizeof(mask), &mask ) == -1 )

    {

    printf("WARNING: Could not set CPU Affinity, continuing...\n");

    }

    如果程序可以执行到这儿,那么我们的线程就已经设置了自己的亲和性(affinity)。调用 sched_setaffinity 会设置由 pid 所引用的进程的 CPU 亲和性(affinity)掩码。如果 pid 为 0,那么就使用当前进程。

    亲和性(affinity)掩码是使用在 mask 中存储的位掩码来表示的。最低位对应于系统中的第一个逻辑处理器,而最高位则对应于系统中最后一个逻辑处理器。

    每个设置的位都对应一个可以合法调度的 CPU,而未设置的位则对应一个不可调度的 CPU。换而言之,进程都被绑定了,只能在那些对应位被设置了的处理器上运行。通常,掩码中的所有位都被置位了。这些线程的亲和性(affinity)都会传递给从它们派生的子进程中。

    注意不应该直接修改位掩码。应该使用下面的宏。虽然在我们的例子中并没有全部使用这些宏,但是在本文中还是详细列出了这些宏,您在自己的程序中可能需要这些宏。清单 3. 间接修改位掩码的宏void CPU_ZERO (cpu_set_t *set)

    这个宏对 CPU 集 set 进行初始化,将其设置为空集。

    void CPU_SET (int cpu, cpu_set_t *set)

    这个宏将 cpu 加入 CPU 集 set 中。

    void CPU_CLR (int cpu, cpu_set_t *set)

    这个宏将 cpu 从 CPU 集 set 中删除。

    int CPU_ISSET (int cpu, const cpu_set_t *set)

    如果 cpu 是 CPU 集 set 的一员,这个宏就返回一个非零值(true),否则就返回零(false)。

    对于本文来说,样例代码会继续让每个线程都执行某些计算量较大的操作。清单 4. 每个线程都执行一个计算敏感的操作/* Now we have a single thread bound to each cpu on the system */

    int computation_res = do_cpu_expensive_op(41);

    cpu_set_t mycpuid;

    sched_getaffinity(0, sizeof(mycpuid), &mycpuid);

    if ( check_cpu_expensive_op(computation_res) )

    {

    printf("SUCCESS: Thread completed, and PASSED integrity check!\n",

    mycpuid);

    ret = TRUE;

    }

    else

    {

    printf("FAILURE: Thread failed integrity check!\n",

    mycpuid);

    ret = FALSE;

    }

    return ret;

    }

    现在您已经了解了在 Linux 2.6 版本的内核中设置 CPU 亲和性(affinity)的基本知识。接下来,我们使用一个 main 程序来封装这些方法,它使用一个用户指定的参数来说明要让多少个 CPU 繁忙。我们可以使用另外一个方法来确定系统中有多少个处理器:

    int NUM_PROCS = sysconf(_SC_NPROCESSORS_CONF);

    这个方法让程序能够自己确定要让多少个处理器保持繁忙,例如缺省让所有的处理器都处于繁忙状态,并允许用户指定系统中实际处理器范围的一个子集。36491_2710049_10.gif

    36491_2710049_2.gif

    当运行前面介绍的 样例程序 时,可以使用很多工具来查看 CPU 是否是繁忙的。如果只是简单地进行测试,可以使用 Linux 命令 top。在运行 top 命令时按下 “1” 键,可以看到每个 CPU 执行进程所占用的百分比。

    展开全文
  • 亲和性(affinity)意味着进程并不会在处理器之间频繁迁移,而硬亲和性(affinity)则意味着进程需要在您指定的处理器上运行。本文介绍了当前的亲和性(affinity)机制,解释为什么和如何使用亲和性(affinity),并给出了...

    解 Linux? 2.6 调度器如何处理 CPU 亲和性(affinity)可以帮助您更好地设计用户空间的应用程序。软亲和性(affinity) 意味着进程并不会在处理器之间频繁迁移,而 硬亲和性(affinity) 则意味着进程需要在您指定的处理器上运行。本文介绍了当前的亲和性(affinity)机制,解释为什么和如何使用亲和性(affinity),并给出了几个样例代码来显示如何使用这种功能。

    简单地说,CPU 亲和性(affinity) 就是进程要在某个给定的 CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性。Linux 内核进程调度器天生就具有被称为 软 CPU 亲和性(affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。

    2.6 版本的 Linux 内核还包含了一种机制,它让开发人员可以编程实现 硬 CPU 亲和性(affinity)。这意味着应用程序可以显式地指定进程在哪个(或哪些)处理器上运行。

    在 Linux 内核中,所有的进程都有一个相关的数据结构,称为 task_struct。这个结构非常重要,原因有很多;其中与 亲和性(affinity)相关度最高的是 cpus_allowed 位掩码。这个位掩码由 n 位组成,与系统中的 n 个逻辑处理器一一对应。 具有 4 个物理 CPU 的系统可以有 4 位。如果这些 CPU 都启用了超线程,那么这个系统就有一个 8 位的位掩码。

    如果为给定的进程设置了给定的位,那么这个进程就可以在相关的 CPU 上运行。因此,如果一个进程可以在任何 CPU 上运行,并且能够根据需要在处理器之间进行迁移,那么位掩码就全是 1。实际上,这就是 Linux 中进程的缺省状态。

    Linux 内核 API 提供了一些方法,让用户可以修改位掩码或查看当前的位掩码:sched_set_affinity() (用来修改位掩码)

    sched_get_affinity() (用来查看当前的位掩码)

    注意,cpu_affinity 会被传递给子线程,因此应该适当地调用 sched_set_affinity。36491_2710049_10.gif

    36491_2710049_2.gif

    通常 Linux 内核都可以很好地对进程进行调度,在应该运行的地方运行进程(这就是说,在可用的处理器上运行并获得很好的整体性能)。内核包含了一些用来检测 CPU 之间任务负载迁移的算法,可以启用进程迁移来降低繁忙的处理器的压力。

    一般情况下,在应用程序中只需使用缺省的调度器行为。然而,您可能会希望修改这些缺省行为以实现性能的优化。让我们来看一下使用硬亲和性(affinity) 的 3 个原因。

    基于大量计算的情形通常出现在科学和理论计算中,但是通用领域的计算也可能出现这种情况。一个常见的标志是您发现自己的应用程序要在多处理器的机器上花费大量的计算时间。

    测试复杂软件是我们对内核的亲和性(affinity)技术感兴趣的另外一个原因。考虑一个需要进行线性可伸缩性测试的应用程序。有些产品声明可以在 使用更多硬件 时执行得更好。

    我们不用购买多台机器(为每种处理器配置都购买一台机器),而是可以:购买一台多处理器的机器

    不断增加分配的处理器

    测量每秒的事务数

    评估结果的可伸缩性

    如果应用程序随着 CPU 的增加可以线性地伸缩,那么每秒事务数和 CPU 个数之间应该会是线性的关系(例如斜线图 —— 请参阅下一节的内容)。这样建模可以确定应用程序是否可以有效地使用底层硬件。36491_2710049_2.gifAmdahl 法则

    Amdahl 法则是有关使用并行处理器来解决问题相对于只使用一个串行处理器来解决问题的加速比的法则。加速比(Speedup) 等于串行执行(只使用一个处理器)的时间除以程序并行执行(使用多个处理器)的时间:T(1)

    S = ------

    T(j)

    其中 T(j) 是在使用 j 个处理器执行程序时所花费的时间。

    Amdahl 法则说明这种加速比在现实中可能并不会发生,但是可以非常接近于该值。对于通常情况来说,我们可以推论出每个程序都有一些串行的组件。随着问题集不断变大,串行组件最终会在优化解决方案时间方面达到一个上限。

    Amdahl 法则在希望保持高 CPU 缓存命中率时尤其重要。如果一个给定的进程迁移到其他地方去了,那么它就失去了利用 CPU 缓存的优势。实际上,如果正在使用的 CPU 需要为自己缓存一些特殊的数据,那么所有其他 CPU 都会使这些数据在自己的缓存中失效。

    因此,如果有多个线程都需要相同的数据,那么将这些线程绑定到一个特定的 CPU 上是非常有意义的,这样就确保它们可以访问相同的缓存数据(或者至少可以提高缓存的命中率)。否则,这些线程可能会在不同的 CPU 上执行,这样会频繁地使其他缓存项失效。

    我们对 CPU 亲和性(affinity)感兴趣的最后一个原因是实时(对时间敏感的)进程。例如,您可能会希望使用硬亲和性(affinity)来指定一个 8 路主机上的某个处理器,而同时允许其他 7 个处理器处理所有普通的系统调度。这种做法确保长时间运行、对时间敏感的应用程序可以得到运行,同时可以允许其他应用程序独占其余的计算资源。

    下面的样例应用程序显示了这是如何工作的。36491_2710049_10.gif

    36491_2710049_2.gif

    现在让我们来设计一个程序,它可以让 Linux 系统非常繁忙。可以使用前面介绍的系统调用和另外一些用来说明系统中有多少处理器的 API 来构建这个应用程序。实际上,我们的目标是编写这样一个程序:它可以让系统中的每个处理器都繁忙几秒钟。可以从后面的“下载”一节中 下载样例程序。清单 1. 让处理器繁忙/* This method will create threads, then bind each to its own cpu. */

    bool do_cpu_stress(int numthreads)

    {

    int ret = TRUE;

    int created_thread = 0;

    /* We need a thread for each cpu we have... */

    while ( created_thread < numthreads - 1 )

    {

    int mypid = fork();

    if (mypid == 0) /* Child process */

    {

    printf("\tCreating Child Thread: #%i\n", created_thread);

    break;

    }

    else /* Only parent executes this */

    {

    /* Continue looping until we spawned enough threads! */ ;

    created_thread++;

    }

    }

    /* NOTE: All threads execute code from here down! */

    正如您可以看到的一样,这段代码只是通过 fork 调用简单地创建一组线程。每个线程都执行这个方法中后面的代码。现在我们让每个线程都将亲和性(affinity)设置为自己的 CPU。清单 2. 为每个线程设置 CPU 亲和性(affinity)cpu_set_t mask;

    /* CPU_ZERO initializes all the bits in the mask to zero. */

    CPU_ZERO( &mask );

    /* CPU_SET sets only the bit corresponding to cpu. */

    CPU_SET( created_thread, &mask );

    /* sched_setaffinity returns 0 in success */

    if( sched_setaffinity( 0, sizeof(mask), &mask ) == -1 )

    {

    printf("WARNING: Could not set CPU Affinity, continuing...\n");

    }

    如果程序可以执行到这儿,那么我们的线程就已经设置了自己的亲和性(affinity)。调用 sched_setaffinity 会设置由 pid 所引用的进程的 CPU 亲和性(affinity)掩码。如果 pid 为 0,那么就使用当前进程。

    亲和性(affinity)掩码是使用在 mask 中存储的位掩码来表示的。最低位对应于系统中的第一个逻辑处理器,而最高位则对应于系统中最后一个逻辑处理器。

    每个设置的位都对应一个可以合法调度的 CPU,而未设置的位则对应一个不可调度的 CPU。换而言之,进程都被绑定了,只能在那些对应位被设置了的处理器上运行。通常,掩码中的所有位都被置位了。这些线程的亲和性(affinity)都会传递给从它们派生的子进程中。

    注意不应该直接修改位掩码。应该使用下面的宏。虽然在我们的例子中并没有全部使用这些宏,但是在本文中还是详细列出了这些宏,您在自己的程序中可能需要这些宏。清单 3. 间接修改位掩码的宏void CPU_ZERO (cpu_set_t *set)

    这个宏对 CPU 集 set 进行初始化,将其设置为空集。

    void CPU_SET (int cpu, cpu_set_t *set)

    这个宏将 cpu 加入 CPU 集 set 中。

    void CPU_CLR (int cpu, cpu_set_t *set)

    这个宏将 cpu 从 CPU 集 set 中删除。

    int CPU_ISSET (int cpu, const cpu_set_t *set)

    如果 cpu 是 CPU 集 set 的一员,这个宏就返回一个非零值(true),否则就返回零(false)。

    对于本文来说,样例代码会继续让每个线程都执行某些计算量较大的操作。清单 4. 每个线程都执行一个计算敏感的操作/* Now we have a single thread bound to each cpu on the system */

    int computation_res = do_cpu_expensive_op(41);

    cpu_set_t mycpuid;

    sched_getaffinity(0, sizeof(mycpuid), &mycpuid);

    if ( check_cpu_expensive_op(computation_res) )

    {

    printf("SUCCESS: Thread completed, and PASSED integrity check!\n",

    mycpuid);

    ret = TRUE;

    }

    else

    {

    printf("FAILURE: Thread failed integrity check!\n",

    mycpuid);

    ret = FALSE;

    }

    return ret;

    }

    现在您已经了解了在 Linux 2.6 版本的内核中设置 CPU 亲和性(affinity)的基本知识。接下来,我们使用一个 main 程序来封装这些方法,它使用一个用户指定的参数来说明要让多少个 CPU 繁忙。我们可以使用另外一个方法来确定系统中有多少个处理器:

    int NUM_PROCS = sysconf(_SC_NPROCESSORS_CONF);

    这个方法让程序能够自己确定要让多少个处理器保持繁忙,例如缺省让所有的处理器都处于繁忙状态,并允许用户指定系统中实际处理器范围的一个子集。36491_2710049_10.gif

    36491_2710049_2.gif

    当运行前面介绍的 样例程序 时,可以使用很多工具来查看 CPU 是否是繁忙的。如果只是简单地进行测试,可以使用 Linux 命令 top。在运行 top 命令时按下 “1” 键,可以看到每个 CPU 执行进程所占用的百分比。

    展开全文
  • 设置线程亲和性,通俗的说法就是将线程绑定到cpu上某一个或多个核上,此处的核是指逻辑核心,非物理核心。 物理核心与逻辑核心的关系,如果开启超线程,一般逻辑核心数=物理核心数*2。 一、SetThreadAffinityMask ...

    设置线程亲和性,通俗的说法就是将线程绑定到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,即可下载。

    在这里插入图片描述

    展开全文
  • 如果需要开发低延迟的网络应用,那应该对线程亲和性(Thread affinity)有所了解。线程亲和性能够强制使你的应用线程运行在特定的一个或多个cpu上。通过这种方式,可以消除操作系统进行调度造成的线程的频繁的上下文...
  • 原文链接 译者:裘卡如果你正在开发低延迟的网络应用,那应该对线程亲和性(Thread affinity)有所了解。线程亲和性能够强制使你的应用线程运行在特定的一个或多个cpu上。通过这种方式,可以消除操作系统进行调度过程...
  • dpdk线程亲和性

    千次阅读 2018-07-04 21:13:51
    Linux对线程亲和性是有支持的,在Linux内核中,所有线程都有一个相关的数据结构,称为task_count,这个结构中和亲和性有关的是cpus_allowed位掩码,这个位掩码由n位组成,n代码逻辑核心的个数。 Linux内核API提供...
  • 线程设置CPU亲和性demo

    2021-10-20 10:52:00
    } 编译运行: @1,无线程cpu亲和性版本,gcc t.c -lpthread @2,有线程cpu亲和性版本,gcc -DAFFINITY_SET t.c -lpthread 从 @1,@2 的结论,可以看出设置线程CPU亲和后,同一个线程,只在绑定的CPU核上调度,而...
  • 线程(14)线程亲和性CPU的亲和性原因:优点 CPU的亲和性 就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,也称为CPU关联性 原因: 在多核运行的机器上,每个CPU本身自己会有缓存,缓存着...
  • linux中查看线程亲和性

    千次阅读 2019-07-19 11:19:53
    每个线程在创建时会有CPU亲和性设置,对于fio,存在一个参数-cpu_allowed,设置fio线程可以在哪些CPU上执行,而对于已经运行的任意线程如何查看CPU亲和性呢?其实也可以查到的,首先需要知道线程所对应的PID,然后...
  • C++多线程2——亲和性

    2021-02-13 10:29:24
    一、多核CPU的结构 服务器的多核结构属于NUMA(Non-Uniform Memory Access,非一致内存访问)架构。这里有一篇很好的介绍NUMA架构理解。...CPU亲和的意思是,假设我们产生40个线程,希望每个线程运行在
  • 文章目录说明实现设置亲和性获取亲和性参考文档 说明 可以将不同的线程设置到不同的cpu上运行,以此来是发挥多核效率 实现 设置亲和性 bool binding_cpu(std::thread& thr, int cpuid) { #ifdef __linux__ int ...
  • 原文地址:Linux下pthread的线程亲和性研究作者:追梦的小鸟一、什么是线程的亲和性: 了解 Linux2.6 调度器如何处理 CPU 亲和性(affinity)可以帮助您更好地设计用户空间的应用程序。软亲和性意味着进程并不会在...
  • 收集所有进程ID和线程ID,然后调用taskset.pname="java" # for examplefor pid in $(pgrep "${pname}")do[ "${pid}" != "" ] || exitecho "PID: ${pid}"for tid in \$(ps --no-headers -ww -p "${pid}" -L -olwp | ...
  • Linux:设置进程(线程)的CPU亲和性 一、进程的CPU亲和性的获取(get)或者设置(set) int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask); int sched_getaffinity(pid_t pid, size_t ...
  • 两种亲和性

    2021-05-26 06:11:28
    1. 进程的亲和性NAMEsched_setaffinity, sched_getaffinity - set and get a process's CPU affinity maskSYNOPSIS#define _GNU_SOURCE#include int sched_setaffinity(pid_t pid, size_t cpusetsize,cpu_set_t *...
  • 一、什么是cpu亲和性(affinity)  CPU的亲和性,就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,也称为CPU关联性;再简单的点的描述就将制定的进程或线程绑定到相应的cpu上;在多核运行的...
  • cpu一般有多个物理核心,但在运行进程和线程时候,可以将其绑定或者指定到某一个或者多个核心上运行。我们在系统上有两种方法可以查看cpu核心数。 1.cat /proc/cpuinfo查看cpu的核心数以及信息,这里以树莓派3B为例...
  • 【c++】-cpu线程亲和性

    2020-04-13 02:42:41
    我读到的写得最完整最好的一篇博文:https://www.cnblogs.com/LubinLew/p/cpu_affinity.html 这篇也很不错:...其他: ...
  • 立即冷血的回复“C ++不知道什么是线程”在邮件列表的山区和处理并行的新闻组问题将永远作为对过去的提醒。 但所有这些都是用C ++ 11结束的。C ++标准委员会意识到语言将无法长时间保持相关性,除非它与时间保持...
  • 线程属性包括是否分离、亲和性、调度策略和优先级等。Linux默认的调度策略是CFS(完全公平调度算法),而Windows是基于优先级抢占式的策略。 鉴于Linux与Windows在线程属性设置的较大差异性,分为调度策略和亲和性两...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,116
精华内容 2,446
关键字:

线程亲和性

友情链接: windrose.rar