线程绑核的简单操作

通过线程绑核可以提升程序运行效率,充分利用多核CPU资源。本文介绍了如何将线程绑定到特定CPU核心,展示了相关C语言代码示例,包括CPU_ZERO、CPU_SET、CPU_CLR、CPU_ISSET等函数的使用,以及pthread_setaffinity_np和pthread_getaffinity_np函数在设置和查看线程CPU亲和性的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了加快程序的运行速度和充分利用CPU资源,我们可以人为将不同线程绑定在不同的cup上,例如有两个线程A,B,其中A已经在CPU0上运行,并且CPU0上还有其他的任务,那么我们可以将线程B绑到CPU1上,这样就可以减轻CPU0的负担,从而充分利用多核CPU。说的明白点,原来是一个CPU作两件事,现在两个CPU同时做两个时,使效率更高。

话不多说,直接上代码:

#define _GNU_SOURCE
#include <sched.h>      //这两个头文件是一起的

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

void *myfun(void *arg)
{
    cpu_set_t mask;
    cpu_set_t get;
  
    CPU_ZERO(&mask);
    CPU_SET(1, &mask);
    if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) 
    {
        fprintf(stderr, "set thread affinity failed\n");
    }

    CPU_ZERO(&get);

    if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0) 
    {
        fprintf(stderr, "get thread affinity failed\n");
    }
    if (CPU_ISSET(1, &get)) 
    {
            
<think>我们正在讨论ThreadX实时操作系统的线程定和抢占控制。根据用户问题,需要将线程定到单独的CPU心并禁用抢占。 首先,ThreadX提供了线程定到特定心的功能,通过`tx_thread_core_exclude_set`或`tx_thread_core_set`等API来实现。同时,禁用抢占可以通过`tx_thread_preemption_disable`来实现,但注意这通常是针对当前线程的。 步骤: 1. 线程到特定心:使用`tx_thread_core_set`函数将指定线程定到指定的CPU心。 2. 禁用抢占:使用`tx_thread_preemption_disable`函数来禁止其他线程抢占当前线程(注意:这通常只影响当前线程,而不是目标线程,所以如果我们要定并禁用抢占的线程不是当前线程,可能需要其他方法)。 但是,请注意,ThreadX的抢占禁用是针对当前线程的,也就是说,调用`tx_thread_preemption_disable`的线程将不会被抢占,直到它重新启用抢占。如果我们想为一个非当前线程禁用抢占,ThreadX并没有直接提供这样的API。通常,实时系统中线程的抢占属性是设计时确定的(通过创建线程时的选项),或者由线程自身控制。 另一种思路:如果我们希望某个心上的线程不被抢占(即该心只运行这一个线程),那么我们可以: a. 将该线程定到该心。 b. 确保没有其他线程定到这个心(或者将该心从其他线程的可用心中排除)。 c. 在该心上禁用时间片轮转(如果使用了时间片)?但ThreadX默认是抢占式的,时间片轮转是可选的。 实际上,在ThreadX中,如果我们想要一个心只运行一个线程且该线程一直运行(不被抢占),我们可以: - 将该线程定到该心。 - 将该心从系统的就绪线程的可用心中排除(使用`tx_thread_core_exclude_set`),这样其他线程就不会在这个心上运行。 - 然后,在该线程中,我们可以禁用抢占(虽然此时没有其他线程在这个心上,但为了确保该线程不会主动让出,我们可以禁用时间片?)实际上,如果该心上只有一个线程,那么调度器不会切换这个心上的线程,所以不需要禁用抢占。但是,如果该线程调用了任何可能挂起的服务(如tx_queue_receive等),那么它仍然可能挂起,然后该心就会空闲(因为没有其他线程可运行)。所以,如果我们希望该线程一直运行,那么它不能执行任何挂起操作。 因此,用户的需求可能需要分两部分: 1. 将线程定到指定心:使用`tx_thread_core_set`。 2. 确保该心上只有这个线程:使用`tx_thread_core_exclude_set`来排除其他线程在该心上运行。 3. 如果希望该线程不被抢占(即一直运行直到主动让出),那么需要确保该线程不调用任何可能挂起的服务,并且禁用时间片轮转(如果启用了)?在ThreadX中,时间片轮转是针对线程的,可以通过`tx_thread_time_slice_change`来设置时间片。如果设置为0,表示该线程的时间片无限,即不会因为时间片用完而让出。但是,如果它调用了挂起函数,仍然会挂起。 所以,具体操作如下: 线程心: - 使用`tx_thread_core_set(thread_ptr, core_id);` 将线程定到心core_id。 排除其他线程在该心上运行: - 对于系统中的其他线程,我们可以通过`tx_thread_core_exclude_set`来设置它们排除在心core_id之外(即这些线程不会在core_id上运行)。但是,这需要为每个线程设置,比较麻烦。另一种方法是,在系统初始化时,将心core_id从默认的可用心掩码中移除,这样新创建的线程默认不会在该心上运行。然后,只将我们指定的线程定到该心。 禁用抢占: - 实际上,在ThreadX中,一旦一个心上只有一个就绪线程,那么该线程就会一直运行,直到它挂起或完成。所以,如果我们确保该线程不会挂起(即它执行一个死循环,且不调用任何可能挂起的服务),那么它就会独占该心。但是,如果该线程需要与外界通信,那么它可能不得不使用挂起操作(如等待消息队列)。在这种情况下,我们无法做到既不让它挂起又能通信。 另一种方法:使用ThreadX的低功耗模式?或者使用中断来处理通信?这可能会复杂化设计。 因此,根据用户需求,如果只是要定并禁止抢占(即该线程独占心且一直运行),我们可以: 1. 创建线程时,将其定到指定心。 2. 将该心从其他所有线程的可用心中排除(或者将其他线程定到其他心)。 3. 在该线程中,设置时间片为0(无限时间片)并且不调用任何挂起函数。 但是,ThreadX的API中,`tx_thread_core_exclude_set`是设置线程排除在哪些心之外,我们可以为其他线程设置排除心core_id。例如: TX_THREAD my_thread; // 创建线程my_thread,并定到心0 tx_thread_core_set(&my_thread, 0); // 对于其他线程,设置它们不能在心0上运行 // 假设有另一个线程other_thread tx_thread_core_exclude_set(&other_thread, 0, TX_AND); 注意:`tx_thread_core_exclude_set`的第三个参数是操作类型(TX_AND, TX_OR, TX_REPLACE),这里我们用TX_AND来将0心从该线程的可用心中移除(实际上,排除心是通过一个掩码,TX_AND表示将当前掩码与传入的掩码进行按位与操作,传入的掩码中0心对应的位为0,其他为1)。但是,更简单的方法是使用TX_REPLACE,直接替换为排除0心的掩码。 然而,为每个线程都设置排除心可能很繁琐,特别是线程数量多的时候。因此,可以在系统初始化时,设置默认的可用心掩码(ThreadX似乎没有提供全局设置默认心掩码的API?)。所以,我们需要在创建每个线程后立即设置排除心0(除了我们指定的线程)。 另外,ThreadX还提供了`tx_system_core_set`函数,用于设置系统心掩码,但这是设置系统支持的心,而不是默认线程的可用心。 因此,我们可能需要手动设置每个线程的可用心掩码(通过`tx_thread_core_exclude_set`或`tx_thread_core_set`)。 关于禁用抢占:在ThreadX中,抢占是系统全局的行为,但我们可以通过`tx_thread_preemption_disable`来禁用当前线程的抢占(即当前线程不会被更高优先级的线程抢占)。但是,这只能由线程自己调用。所以,如果我们希望定的线程不被抢占,那么需要在该线程的入口函数中调用`tx_thread_preemption_disable`。注意,这只会影响该线程自身(即它不会被抢占),但其他心上的线程仍然可以被抢占(除非它们也调用了禁用抢占)。 总结步骤: 1. 创建并启动目标线程(假设为thread_x)。 2. 将thread_x定到心core_id:`tx_thread_core_set(&thread_x, core_id);` 3. 在thread_x的入口函数中,调用`tx_thread_preemption_disable();`来禁止其他线程抢占它(注意:这只能防止该线程被更高优先级的线程抢占,但并不能阻止该线程主动挂起)。 4. 为了确保心core_id上只有thread_x,我们需要将其他所有线程排除在心core_id之外。这可以通过在创建每个其他线程后,调用`tx_thread_core_exclude_set`来设置它们不能运行在core_id上。例如: // 假设core_id=0,则掩码中第0位为0,其他位为1 ULONG exclude_core_mask = ~(1UL << core_id); tx_thread_core_exclude_set(&other_thread, exclude_core_mask, TX_REPLACE); 5. 另外,在thread_x中,设置时间片为无限:`tx_thread_time_slice_change(&thread_x, TX_NO_TIME_SLICE, &old_time_slice);` 这样,心core_id上只有thread_x,且该线程不会被抢占(因为禁用了抢占),时间片无限(不会因为时间片用完而让出),并且我们确保它不会主动挂起(即不在该线程中调用任何可能挂起的服务),那么它就会一直运行。 但是,如果thread_x需要与其他线程通信,它可能必须使用挂起操作。在这种情况下,我们无法避免挂起。此时,当它挂起时,心core_id会空闲(因为没有其他线程可以运行在该心上),直到thread_x就绪。这可能会造成心浪费。 因此,用户需要权衡:要么让thread_x一直运行(不挂起),要么接受它在等待时会挂起。 最后,注意:ThreadX的API具体使用请参考官方文档,因为不同版本可能略有差异。 代码示例: 假设我们有两个心(0和1),我们想将线程`my_thread`定到心0,并禁止其他线程心0上运行,同时禁止抢占并设置无限时间片。 步骤: 1. 创建线程`my_thread`,并启动。 2. 线程心0: tx_thread_core_set(&my_thread, 0); 3. 在`my_thread`的入口函数中: void my_thread_entry(ULONG param) { // 禁用抢占 tx_thread_preemption_disable(); // 设置时间片为无限 tx_thread_time_slice_change(&my_thread, TX_NO_TIME_SLICE, &old_time_slice); // 然后执行该线程的任务(注意不要挂起) while(1) { // 做任务,不能调用tx_queue_receive, tx_semaphore_get等可能挂起的函数 } } 4. 对于其他所有线程(比如已经存在的线程和之后创建的线程),我们需要设置它们不能在心0上运行。例如,在创建线程`other_thread`后: ULONG exclude_mask = ~(1 << 0); // 掩码:0心对应的位为0,其他为1 tx_thread_core_exclude_set(&other_thread, exclude_mask, TX_REPLACE); 注意:`tx_thread_core_exclude_set`的第三个参数为TX_REPLACE,表示用传入的掩码替换线程当前的可用心掩码。 但是,如果系统中有很多线程,这样设置可能很麻烦。因此,可以在系统初始化完成后,遍历所有已创建的线程(ThreadX没有提供遍历线程的API,所以需要我们自己记录线程指针)来设置。 另外,如果系统支持动态创建线程,那么需要在每次创建线程后立即设置排除心0。 最后,关于心和排除心的API,请确保在ThreadX的文档中确认,因为函数名和参数可能因版本而异。 以上方法实现了将线程定到单独心并禁止抢占(通过独占心和禁用自身抢占)。 注意:禁用抢占(`tx_thread_preemption_disable`)是针对当前线程的,所以必须在目标线程中调用。如果目标线程已经运行,我们无法从外部为其禁用抢占。 因此,用户需要根据实际需求调整。</think>### ThreadX 线程心与禁用抢占方法 在 ThreadX 实时操作系统中,将线程定到特定 CPU 心并禁用抢占需要结合两种关键机制:**定**和**抢占控制**。以下是具体实现步骤: --- #### **1. 线程定** 使用 `tx_thread_core_set` API 将线程固定到指定 CPU 心: ```c UINT tx_thread_core_set( TX_THREAD *thread_ptr, UINT core_index ); ``` - **参数说明**: - `thread_ptr`:目标线程控制块指针 - `core_index`:目标心索引(从 0 开始) - **示例代码**: ```c TX_THREAD my_thread; UINT status = tx_thread_core_set(&my_thread, 2); // 定到心2 if (status != TX_SUCCESS) { // 错误处理 } ``` > **关键点**:定后线程仅在该心执行,但**仍可能被同心更高优先级线程抢占**。 --- #### **2. 禁用线程抢占** 通过优先级调整和抢占控制实现独占心: - **方法1:提升线程至最高优先级** ```c tx_thread_priority_change(&my_thread, 0, &old_priority); ``` - 将优先级设为 0(ThreadX 最高优先级) - 确保无更高优先级线程可抢占 - **方法2:禁用时间片轮转** ```c tx_thread_time_slice_change(&my_thread, TX_NO_TIME_SLICE, &old_slice); ``` - `TX_NO_TIME_SLICE` 禁止时间片到期切换 - **方法3:全局抢占禁用 (谨慎使用)** ```c TX_INTERRUPT_SAVE_AREA TX_DISABLE // 关键代码区 TX_RESTORE ``` - 通过中断禁用实现原子操作[^2] --- #### **完整实现示例** ```c void setup_dedicated_core_thread() { TX_THREAD dedicated_thread; // 创建线程(省略线程创建细节) tx_thread_create(&dedicated_thread, ...); // 定到心3 tx_thread_core_set(&dedicated_thread, 3); // 提升至最高优先级 UINT old_prio; tx_thread_priority_change(&dedicated_thread, 0, &old_prio); // 禁用时间片轮转 ULONG old_slice; tx_thread_time_slice_change(&dedicated_thread, TX_NO_TIME_SLICE, &old_slice); } ``` --- #### **注意事项** 1. **抢占禁用范围** - 优先级法仅防止**同心**抢占 - 需确保所有线程正确心[^1] 2. **中断处理影响** - 硬件中断仍可中断线程执行 - 需在中断服务程序(ISR)中避免长时间操作 3. **系统负载均衡** - 长期独占心可能导致其他心过载 - 建议仅对实时性要求极高的线程使用 > **关键参考**:ThreadX 的线程调度遵循严格优先级策略,定需配合优先级管理才能实现完全独占[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值