linux中条件变量和信号量有什么区别

来源:Golang编程网作者:弦宿​头衔:草根站长
导读:本期聚焦于小伙伴创作的《linux中条件变量和信号量有什么区别》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《linux中条件变量和信号量有什么区别》有用,将其分享出去将是对创作者最好的鼓励。

在Linux多线程开发过程中,条件变量和信号量是两种核心的线程同步机制,二者虽然都能实现线程间的协作与等待,但在设计目标、工作逻辑和适用场景上存在明显差异,理解这些差异能帮助开发者更合理地选择同步方案。

核心定义与本质差异

条件变量的核心作用是等待某个条件成立,它需要和互斥锁配合使用,本质是一个线程等待队列,当某个共享数据的状态满足特定条件时,通知等待的线程继续执行。

信号量的本质是一个计数器,用来控制同时访问某个共享资源的线程数量,计数器的值表示当前可用的资源数量,线程获取资源时计数器减1,释放资源时计数器加1。

工作原理对比

条件变量的工作流程

条件变量的使用必须绑定一个互斥锁,流程如下:

  • 线程先获取互斥锁,检查共享条件是否满足
  • 若条件不满足,调用等待函数释放互斥锁并进入阻塞状态,放入条件变量的等待队列
  • 其他线程修改共享条件后,调用唤醒函数通知等待队列中的线程
  • 被唤醒的线程重新获取互斥锁,再次检查条件是否满足,满足则继续执行

信号量的工作流程

信号量独立工作,不需要绑定互斥锁,流程如下:

  • 初始化信号量时设置初始计数器值,代表可用资源数量
  • 线程需要访问资源时调用P操作(等待操作),计数器减1,若计数器小于0则线程阻塞
  • 线程使用完资源后调用V操作(释放操作),计数器加1,若计数器小于等于0则唤醒一个阻塞的线程

基础使用示例

条件变量使用示例

以下代码实现了一个简单的生产者消费者模型,生产者生产数据后通知消费者,消费者等待数据就绪后消费:

#include <stdio.h>
#include <pthread.h>

// 定义互斥锁和条件变量
pthread_mutex_t mutex;
pthread_cond_t cond;
int data_ready = 0; // 共享条件:数据是否就绪

// 生产者线程函数
void* producer(void* arg) {
    pthread_mutex_lock(&mutex);
    data_ready = 1; // 修改共享条件
    printf("生产者生产数据完成n");
    pthread_cond_signal(&cond); // 唤醒等待条件变量的线程
    pthread_mutex_unlock(&mutex);
    return NULL;
}

// 消费者线程函数
void* consumer(void* arg) {
    pthread_mutex_lock(&mutex);
    // 循环检查条件,避免虚假唤醒
    while (data_ready == 0) {
        printf("消费者等待数据n");
        pthread_cond_wait(&cond, &mutex); // 释放互斥锁并等待
    }
    printf("消费者消费数据n");
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t prod_tid, cons_tid;
    // 初始化互斥锁和条件变量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    // 创建线程
    pthread_create(&cons_tid, NULL, consumer, NULL);
    sleep(1); // 让消费者先启动进入等待
    pthread_create(&prod_tid, NULL, producer, NULL);
    // 等待线程结束
    pthread_join(prod_tid, NULL);
    pthread_join(cons_tid, NULL);
    // 销毁同步对象
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    return 0;
}

信号量使用示例

以下代码使用信号量控制同时访问共享资源的线程数量,初始信号量设为2,最多允许2个线程同时访问资源:

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

sem_t sem; // 定义信号量

// 线程函数
void* worker(void* arg) {
    int thread_id = *(int*)arg;
    sem_wait(&sem); // P操作:获取资源,计数器减1
    printf("线程%d获取资源,开始工作n", thread_id);
    sleep(2); // 模拟工作耗时
    printf("线程%d工作完成,释放资源n", thread_id);
    sem_post(&sem); // V操作:释放资源,计数器加1
    return NULL;
}

int main() {
    pthread_t tids[5];
    int thread_ids[5];
    // 初始化信号量,初始值为2,代表2个可用资源
    sem_init(&sem, 0, 2);
    // 创建5个线程
    for (int i = 0; i < 5; i++) {
        thread_ids[i] = i + 1;
        pthread_create(&tids[i], NULL, worker, &thread_ids[i]);
    }
    // 等待所有线程结束
    for (int i = 0; i < 5; i++) {
        pthread_join(tids[i], NULL);
    }
    // 销毁信号量
    sem_destroy(&sem);
    return 0;
}

关键差异总结

对比维度条件变量信号量
核心作用等待特定条件成立,用于线程间的条件通知控制并发访问资源的线程数量,用于资源计数
依赖组件必须配合互斥锁使用独立使用,无需绑定其他同步对象
计数器特性无计数器,仅维护等待队列自带计数器,记录可用资源数量
唤醒逻辑可以唤醒一个或所有等待线程每次V操作最多唤醒一个阻塞线程
适用场景生产者消费者模型、等待某个状态变化限流、控制并发数、资源池管理

使用注意事项

使用条件变量时,等待条件的判断必须放在循环中,避免虚假唤醒问题,即线程被唤醒后条件可能仍未满足,需要重新检查。

信号量的初始值需要根据实际可用资源数量设置,P操作和V操作必须成对出现,否则可能导致计数器异常,引发线程永久阻塞或者资源访问冲突。

如果需要在多线程间传递简单的状态通知,且通知的场景是某个条件是否满足,优先选择条件变量;如果需要限制同时访问资源的线程数量,优先选择信号量。

条件变量信号量pthread_cond_tsem_t线程同步修改时间:2026-06-22 11:54:53

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。