You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何pthread_barrier_wait不考虑线程优先级?如何确保高优线程先执行?

问题分析与解决方案

这是POSIX线程的预期行为吗?

是的,这完全符合POSIX标准的预期行为。pthread_barrier_wait()的设计仅保证所有线程都到达同步点后再继续执行,但POSIX没有规定线程被唤醒后的执行顺序必须遵循优先级。具体来说,当屏障的计数达到阈值时,底层实现可以以任意顺序唤醒等待的线程——哪怕高优先级线程也可能晚于低优先级线程被调度执行。

为什么会出现这种情况?

你的代码中,虽然给两个线程设置了不同的SCHED_FIFO优先级且绑定到同一个核心,但屏障唤醒的逻辑并不感知线程优先级。当两个线程都调用pthread_barrier_wait()后,屏障的底层实现可能会按线程进入等待的顺序、或是内核调度的随机顺序来唤醒线程,这就导致低优先级的task_2偶尔会先被唤醒并执行start_task_2()。

如何确保task_1始终先于task_2启动?

你需要在屏障同步之后,再添加一个优先级感知的同步机制,强制高优先级线程先执行。下面提供两种可靠的实现方案:

方案一:使用优先级保护互斥锁(PTHREAD_PRIO_PROTECT)

这种方法利用SCHED_FIFO调度策略下高优先级线程会优先获取互斥锁的特性,确保task_1先执行:

  1. 初始化一个带有PTHREAD_PRIO_PROTECT属性的互斥锁,将优先级天花板设置为task_1的最高优先级(MAX_PRIORITY)。
  2. 两个线程在pthread_barrier_wait()之后,都尝试获取这个互斥锁。由于task_1优先级更高,它会先拿到锁并执行任务,释放锁后task_2才能继续。

修改后的关键代码如下:

#include <pthread.h>
#include <sched.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>

#define MAX_PRIORITY 99
#define MIN_PRIORITY 1

pthread_t threads[2];
pthread_barrier_t barrier;
pthread_mutex_t priority_mutex;

void set_priority(int priority, int t_id){
    int policy = SCHED_FIFO;
    struct sched_param param;
    param.sched_priority = priority;
    pthread_attr_t attr;
    pthread_attr_init (&attr);
    pthread_setschedparam(pthread_self(), policy, &param);
    pthread_getschedparam(pthread_self(), &policy, &param);
}

int set_core(int core_id) {
    int num_cores = sysconf(_SC_NPROCESSORS_ONLN);
    if (core_id < 0 || core_id >= num_cores)
        return EINVAL;
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(core_id, &cpuset);
    pthread_t current_thread = pthread_self();
    return pthread_setaffinity_np(current_thread, sizeof(cpu_set_t), &cpuset);
}

void create_task(int task_number, void *task) {
    int rc = pthread_create(&threads[task_number - 1], NULL, task, NULL);
    if(rc != 0) {
        printf("pthread_create(%d) error %d\n", task_number - 1, rc);
        pthread_exit(0) ;
    }
}

void schedule_task(int task_number, int priority) {
    set_core(2); //running tasks only in 2nd processor core
    set_priority(priority, task_number);
}

void start_task_1() {
    printf("Task 1 Started \n");
    sleep(1); //do task 1
    printf("Task 1 Endeded\n");
}

void start_task_2() {
    printf("Task 2 Started \n");
    sleep(1); //do task 2
    printf("Task 2 Endeded\n");
}

void task_1(void *thread_param) {
    schedule_task(1, MAX_PRIORITY);
    pthread_barrier_wait(&barrier);
    
    pthread_mutex_lock(&priority_mutex);
    start_task_1();
    pthread_mutex_unlock(&priority_mutex);
    
    pthread_exit(NULL);
}

void task_2(void *thread_param) {
    schedule_task(2, MIN_PRIORITY);
    pthread_barrier_wait(&barrier);
    
    pthread_mutex_lock(&priority_mutex);
    start_task_2();
    pthread_mutex_unlock(&priority_mutex);
    
    pthread_exit(NULL);
}

int main() {
    pthread_barrier_init(&barrier, NULL, 2);
    
    // 初始化优先级保护互斥锁
    pthread_mutexattr_t mutex_attr;
    pthread_mutexattr_init(&mutex_attr);
    pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_PROTECT);
    pthread_mutexattr_setprioceiling(&mutex_attr, MAX_PRIORITY);
    pthread_mutex_init(&priority_mutex, &mutex_attr);
    pthread_mutexattr_destroy(&mutex_attr);
    
    create_task(1, task_1);
    create_task(2, task_2);
    
    for (int i = 0; i < 2; i++) {
        pthread_join(threads[i], NULL);
    }
    
    pthread_barrier_destroy(&barrier);
    pthread_mutex_destroy(&priority_mutex);
    
    return 0;
}

方案二:使用条件变量让task_2等待task_1启动

这种方法更直接,让task_1在屏障同步后先启动,再主动通知task_2开始执行:

  1. 初始化一个互斥锁和条件变量。
  2. task_1在pthread_barrier_wait()后先执行任务,然后发送条件信号;task_2在屏障同步后,等待信号再执行任务。

关键代码修改如下:

#include <pthread.h>
#include <sched.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>

#define MAX_PRIORITY 99
#define MIN_PRIORITY 1

pthread_t threads[2];
pthread_barrier_t barrier;
pthread_mutex_t cv_mutex;
pthread_cond_t start_cond;
int task1_started = 0;

// 保留原有set_priority、set_core、create_task、schedule_task函数...

void start_task_1() {
    printf("Task 1 Started \n");
    sleep(1); //do task 1
    printf("Task 1 Endeded\n");
}

void start_task_2() {
    printf("Task 2 Started \n");
    sleep(1); //do task 2
    printf("Task 2 Endeded\n");
}

void task_1(void *thread_param) {
    schedule_task(1, MAX_PRIORITY);
    pthread_barrier_wait(&barrier);
    
    start_task_1();
    
    // 通知task_2可以启动
    pthread_mutex_lock(&cv_mutex);
    task1_started = 1;
    pthread_cond_signal(&start_cond);
    pthread_mutex_unlock(&cv_mutex);
    
    pthread_exit(NULL);
}

void task_2(void *thread_param) {
    schedule_task(2, MIN_PRIORITY);
    pthread_barrier_wait(&barrier);
    
    // 等待task_1启动的信号
    pthread_mutex_lock(&cv_mutex);
    while (!task1_started) {
        pthread_cond_wait(&start_cond, &cv_mutex);
    }
    pthread_mutex_unlock(&cv_mutex);
    
    start_task_2();
    pthread_exit(NULL);
}

int main() {
    pthread_barrier_init(&barrier, NULL, 2);
    pthread_mutex_init(&cv_mutex, NULL);
    pthread_cond_init(&start_cond, NULL);
    
    create_task(1, task_1);
    create_task(2, task_2);
    
    for (int i = 0; i < 2; i++) {
        pthread_join(threads[i], NULL);
    }
    
    pthread_barrier_destroy(&barrier);
    pthread_mutex_destroy(&cv_mutex);
    pthread_cond_destroy(&start_cond);
    
    return 0;
}

额外优化建议

你的代码中,线程优先级是在线程内部通过pthread_setschedparam()设置的,这种方式存在竞态风险(线程可能在设置优先级前就开始执行)。更可靠的方式是在创建线程时,通过pthread_attr_t直接指定调度参数:

void create_task(int task_number, void *task, int priority) {
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    
    // 设置调度策略为SCHED_FIFO
    pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
    
    struct sched_param param;
    param.sched_priority = priority;
    pthread_attr_setschedparam(&attr, &param);
    
    // 强制使用指定的调度属性,不继承主线程属性
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    
    int rc = pthread_create(&threads[task_number - 1], &attr, task, NULL);
    if(rc != 0) {
        printf("pthread_create(%d) error %d\n", task_number - 1, rc);
        pthread_exit(0);
    }
    
    pthread_attr_destroy(&attr);
}

这样可以确保线程从创建之初就拥有指定的优先级,避免潜在的竞态问题。

内容的提问来源于stack exchange,提问作者Bruno Pessanha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:06:04