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

关于pthread条件变量理解与多线程同步启动的技术咨询

理清pthread同步逻辑,实现多调度策略线程的统一启动测试

我完全懂你在折腾pthread互斥锁、条件变量时的迷茫——这俩组合的逻辑确实容易绕,尤其是你想精准控制9个不同调度策略的线程同时启动来测效率的场景。先帮你把核心逻辑捋顺,再给你一套可运行的代码示例,一步步拆解问题。

先纠正你的核心逻辑误区

你提到“主函数启动后先调用pthread_lock(&mutex),再通过pthread_cond_wait(&cond, &mutex)确保线程在我发出指令前不会启动”——这个逻辑是反的!如果主线程先挂在pthread_cond_wait上,那谁来唤醒它?正确的姿势应该是让子线程先阻塞在条件变量上,主线程等所有子线程创建完毕后,统一发信号唤醒它们。

条件变量的正确用法是:

  • 用一个共享条件标志(比如volatile int start_flag = 0)来表示“是否允许启动”
  • 子线程启动后,先加锁,然后循环检查这个标志:如果没到启动时机,就调用pthread_cond_wait挂起(挂起时会自动释放互斥锁,被唤醒时又会重新获取锁)
  • 主线程等所有子线程创建完成后,加锁、设置标志为1、调用pthread_cond_broadcast唤醒所有等待的子线程,最后解锁

实现步骤与代码示例

1. 全局变量与初始化

首先定义共享的互斥锁、条件变量,以及启动标志:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>
#include <sys/time.h>

// 同步用的全局变量
pthread_mutex_t mutex;
pthread_cond_t cond;
volatile int start_flag = 0;

// 记录每个线程的耗时
typedef struct {
    int thread_id;
    int policy;
    struct timeval start_time;
    struct timeval end_time;
} ThreadData;

2. 线程函数逻辑

每个线程先阻塞等待启动信号,被唤醒后执行测试任务(这里用循环模拟耗时操作),记录时间:

void* thread_func(void* arg) {
    ThreadData* data = (ThreadData*)arg;

    // 加锁,准备等待启动信号
    pthread_mutex_lock(&mutex);
    // 用while循环避免虚假唤醒!这是条件变量的关键细节
    while (!start_flag) {
        pthread_cond_wait(&cond, &mutex);
    }
    // 被唤醒后,记录启动时间
    gettimeofday(&data->start_time, NULL);

    // 解锁,因为后续任务不需要持有锁(锁只用来同步启动时机)
    pthread_mutex_unlock(&mutex);

    // 模拟耗时任务:比如循环1e8次
    long long i;
    for (i = 0; i < 100000000; i++);

    // 记录结束时间
    gettimeofday(&data->end_time, NULL);

    return NULL;
}

3. 主线程逻辑:创建线程、设置调度策略、统一启动

主线程负责初始化同步变量、创建9个不同调度策略的线程,最后唤醒它们并统计结果:

int main() {
    int i;
    pthread_t threads[9];
    ThreadData thread_data[9];
    // 三种调度策略,每个策略3个线程
    int policies[] = {SCHED_FIFO, SCHED_RR, SCHED_OTHER,
                      SCHED_FIFO, SCHED_RR, SCHED_OTHER,
                      SCHED_FIFO, SCHED_RR, SCHED_OTHER};
    struct sched_param param;

    // 初始化互斥锁和条件变量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    // 创建9个线程,设置不同的调度策略
    for (i = 0; i < 9; i++) {
        thread_data[i].thread_id = i;
        thread_data[i].policy = policies[i];

        // 设置线程调度参数:SCHED_FIFO和SCHED_RR需要设置优先级
        param.sched_priority = (policies[i] == SCHED_OTHER) ? 0 : 10;

        // 创建线程前先设置属性
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        // 设置调度策略
        pthread_attr_setschedpolicy(&attr, policies[i]);
        // 设置调度参数
        pthread_attr_setschedparam(&attr, &param);
        // 允许线程继承调度属性
        pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);

        // 创建线程
        if (pthread_create(&threads[i], &attr, thread_func, &thread_data[i]) != 0) {
            perror("pthread_create failed");
            exit(EXIT_FAILURE);
        }
        pthread_attr_destroy(&attr);
    }

    // 等所有线程都创建完毕,准备统一启动
    printf("All threads created, ready to start...\n");
    sleep(1); // 给线程足够时间进入等待状态

    // 加锁、设置启动标志、唤醒所有线程
    pthread_mutex_lock(&mutex);
    start_flag = 1;
    pthread_cond_broadcast(&cond);
    pthread_mutex_unlock(&mutex);

    // 等待所有线程完成
    for (i = 0; i < 9; i++) {
        pthread_join(threads[i], NULL);
    }

    // 统计并打印结果
    printf("\nThread performance results:\n");
    for (i = 0; i < 9; i++) {
        long long elapsed = (thread_data[i].end_time.tv_sec - thread_data[i].start_time.tv_sec) * 100000LL +
                            (thread_data[i].end_time.tv_usec - thread_data[i].start_time.tv_usec);
        char* policy_name;
        switch (thread_data[i].policy) {
            case SCHED_FIFO: policy_name = "SCHED_FIFO"; break;
            case SCHED_RR: policy_name = "SCHED_RR"; break;
            case SCHED_OTHER: policy_name = "SCHED_OTHER"; break;
            default: policy_name = "UNKNOWN";
        }
        printf("Thread %d (%s): %lld us\n", thread_data[i].thread_id, policy_name, elapsed);
    }

    // 清理资源
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    return 0;
}

关键细节提醒

  • 虚假唤醒:一定要用while (!start_flag)而不是if,因为线程可能会被意外唤醒(比如系统信号),这时候需要重新检查条件
  • 调度策略权限:SCHED_FIFO和SCHED_RR需要以root权限运行程序,否则会设置失败
  • 互斥锁的释放时机:子线程被唤醒后要立即解锁,避免长时间持有锁影响其他线程
  • 线程属性的设置:必须用pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED),否则线程会继承主线程的调度策略,设置无效

测试效率的注意点

  • 可以多次运行取平均值,避免单次运行的偶然性
  • 可以调整模拟任务的复杂度,让耗时更明显,方便对比
  • 可以观察CPU使用率、线程切换次数等系统指标,辅助判断调度策略的效率

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:53:35