pthread_cond_broadcast的概念和使用案例

pthread_cond_broadcast 是 POSIX 线程(Pthreads)库中用于条件变量(Condition Variable)操作的函数,定义在 <pthread.h> 头文件中。它的核心作用是唤醒所有等待在某个条件变量上的线程,通常用于多线程同步的场景。


核心概念

  1. 条件变量(Condition Variable)
    条件变量是线程同步的一种机制,允许线程在某个条件不满足时挂起(等待),并在条件可能满足时被唤醒。它必须与**互斥锁(Mutex)**配合使用。

  2. pthread_cond_broadcast 的功能

    • 唤醒所有正在等待该条件变量的线程。
    • pthread_cond_signal(仅唤醒一个线程)不同,broadcast 会唤醒所有线程,适用于需要多个线程同时响应某个条件变化的场景。
  3. 典型使用模式

    // 线程等待条件
    pthread_mutex_lock(&mutex);
    while (condition_is_false) {pthread_cond_wait(&cond, &mutex);
    }
    // 处理条件满足后的操作
    pthread_mutex_unlock(&mutex);// 另一线程触发条件
    pthread_mutex_lock(&mutex);
    // 修改条件
    condition_is_true = 1;
    pthread_cond_broadcast(&cond); // 唤醒所有等待线程
    pthread_mutex_unlock(&mutex);
    

使用案例:任务队列的线程池

以下示例演示了一个简单的线程池,多个工作线程从任务队列中获取任务。当有新任务加入时,主线程通过 pthread_cond_broadcast 唤醒所有工作线程。

代码实现
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>#define MAX_TASKS 10
#define NUM_WORKERS 3// 任务结构体
typedef struct {int id;
} Task;// 共享任务队列
Task task_queue[MAX_TASKS];
int task_count = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;// 工作线程函数
void* worker(void* arg) {int worker_id = *(int*)arg;while (1) {pthread_mutex_lock(&mutex);// 等待任务队列非空while (task_count == 0) {printf("Worker %d: waiting...\n", worker_id);pthread_cond_wait(&cond, &mutex);}// 取出任务Task task = task_queue[--task_count];printf("Worker %d: processing task %d\n", worker_id, task.id);pthread_mutex_unlock(&mutex);// 模拟任务处理sleep(1);}return NULL;
}int main() {pthread_t workers[NUM_WORKERS];int worker_ids[NUM_WORKERS];// 创建工作线程for (int i = 0; i < NUM_WORKERS; i++) {worker_ids[i] = i + 1;pthread_create(&workers[i], NULL, worker, &worker_ids[i]);}// 主线程添加任务for (int i = 0; i < 5; i++) {pthread_mutex_lock(&mutex);if (task_count < MAX_TASKS) {Task new_task = { .id = i + 1 };task_queue[task_count++] = new_task;printf("Main: added task %d\n", new_task.id);// 唤醒所有工作线程pthread_cond_broadcast(&cond);}pthread_mutex_unlock(&mutex);sleep(1);}// 等待工作线程结束(此处简化,实际需添加退出逻辑)for (int i = 0; i < NUM_WORKERS; i++) {pthread_join(workers[i], NULL);}pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}

代码解析

  1. 共享资源保护

    • task_queuetask_count 是共享资源,通过 pthread_mutex_t mutex 保护。
    • 线程在访问任务队列前必须获取锁。
  2. 条件变量的等待与唤醒

    • 工作线程调用 pthread_cond_wait 挂起,直到任务队列非空。
    • 主线程添加任务后,调用 pthread_cond_broadcast 唤醒所有等待的线程。
  3. broadcast 的必要性
    如果使用 pthread_cond_signal(仅唤醒一个线程),可能导致任务处理效率低下。例如:

    • 添加多个任务时,需要多次调用 signal
    • 使用 broadcast 可一次性唤醒所有线程,让它们竞争处理任务,提高并发性。

关键注意事项

  1. 虚假唤醒(Spurious Wakeup)
    线程可能在没有收到信号的情况下被唤醒,因此必须用 while 循环检查条件,而非 if 语句:

    while (task_count == 0) {pthread_cond_wait(&cond, &mutex);
    }
    
  2. 锁的释放与重新获取

    • pthread_cond_wait自动释放锁并挂起线程。
    • 被唤醒后,线程会重新获取锁,继续执行后续代码。
  3. 性能权衡

    • broadcast 会唤醒所有线程,可能导致资源竞争。如果只需唤醒一个线程,优先使用 pthread_cond_signal

典型应用场景

  1. 资源池管理
    当资源(如数据库连接)可用时,唤醒所有等待线程竞争资源。

  2. 事件广播
    例如,多个线程需要同时响应某个全局状态的变化(如配置文件更新)。

  3. 生产者-消费者模型
    生产者一次性添加多个任务时,唤醒所有消费者线程。


常见问题

  1. 何时用 broadcast,何时用 signal

    • 如果条件的变化可能让多个线程同时满足执行条件,用 broadcast(如任务队列新增多个任务)。
    • 如果条件的变化只允许一个线程继续执行,用 signal(如单资源可用)。
  2. 为什么用 while 检查条件?
    防止虚假唤醒或条件被其他线程修改。

  3. 如何终止等待的线程?
    通常设置一个退出标志,并在终止前调用 broadcast

    // 设置退出标志
    pthread_mutex_lock(&mutex);
    exit_flag = 1;
    pthread_cond_broadcast(&cond);
    pthread_mutex_unlock(&mutex);
    

通过合理使用 pthread_cond_broadcast,可以实现高效的多线程同步,尤其适用于需要批量唤醒线程的场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/69005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

爬虫学习笔记之Robots协议相关整理

定义 Robots协议也称作爬虫协议、机器人协议&#xff0c;全名为网络爬虫排除标准&#xff0c;用来告诉爬虫和搜索引擎哪些页面可以爬取、哪些不可以。它通常是一个叫做robots.txt的文本文件&#xff0c;一般放在网站的根目录下。 robots.txt文件的样例 对有所爬虫均生效&#…

Unity游戏(Assault空对地打击)开发(4) 碰撞体和刚体的添加

前言 飞机和世界的大小关系不太对&#xff0c;我稍微缩小了一下飞机。 详细步骤 选中所有地形对象&#xff0c;如果没有圈起的部分&#xff0c;点击Add Component搜索添加。 接着选中Player对象&#xff0c;添加这两个组件&#xff0c;最好&#xff08;仅对于本项目开发&#x…

【Linux】从硬件到软件了解进程

个人主页~ 从硬件到软件了解进程 一、冯诺依曼体系结构二、操作系统三、操作系统进程管理1、概念2、PCB和task_struct3、查看进程4、通过系统调用fork创建进程&#xff08;1&#xff09;简述&#xff08;2&#xff09;系统调用生成子进程的过程〇提出问题①fork函数②父子进程关…

C语言教学第三课:运算符与表达式

一、课程导入 同学们&#xff0c;上节课我们学习了变量和数据类型&#xff0c;这些是C语言的基础。今天&#xff0c;我们将继续深入学习C语言中的运算符与表达式。运算符是C语言中用于执行各种操作的符号&#xff0c;而表达式则是由变量、常量和运算符组成的有意义的组合。通过…

Maven全解析:从基础到精通的实战指南

概念&#xff1a; Maven 是跨平台的项目管理工具。主要服务基于 Java 平台的构建&#xff0c;依赖管理和项目信息管理项目构建&#xff1a;高度自动化&#xff0c;跨平台&#xff0c;可重用的组件&#xff0c;标准化的流程 依赖管理&#xff1a; 对第三方依赖包的管理&#xf…

MATLAB实现单层竞争神经网络数据分类

一.单层竞争神经网络介绍 单层竞争神经网络&#xff08;Single-Layer Competitive Neural Network&#xff09;是一种基于竞争学习的神经网络模型&#xff0c;主要用于数据分类和模式识别。其核心思想是通过神经元之间的竞争机制&#xff0c;使得网络能够自动学习输入数据的特…

Weevely代码分析

亲测php5和php8都无效&#xff0c;只有php7有效 ailx10 1949 次咨询 4.9 网络安全优秀回答者 互联网行业 安全攻防员 去咨询 上一次做weevely实验可以追溯到2020年&#xff0c;当时还是weevely3.7&#xff0c;现在的是weevely4 生成php网页木马依然差不多…… php菜刀we…

【AI大模型】DeepSeek API大模型接口实现

目录 一、DeepSeek发展历程 2023 年&#xff1a;创立与核心技术突破 2024 年&#xff1a;开源生态与行业落地 2025 年&#xff1a;多模态与全球化布局 性能对齐 OpenAI-o1 正式版​ 二、API接口调用 1.DeepSeek-V3模型调用 2.DeepSeek-R1模型调用 三、本地化部署接口调…

具身智能-强化学习-强化学习基础-马尔可夫

文章目录 参考强化学习基础强化学习特点reward函数两种强化学习两种策略&#xff1a;探索&#xff08;Exploration&#xff09; vs. 利用&#xff08;Exploitation&#xff09;gym库的使用 马尔可夫马尔可夫过程马尔可夫奖励过程&#xff08;Markov Reward Process, MRP&#x…

半导体器件与物理篇5 mosfet及相关器件

认识mos二极管 MOS二极管是研究半导体表面特性最有用的器件之一。MOS二极管可作为存储电容器&#xff0c;并且是电荷耦合器件(CCD)的基本结构单元。 MOS二极管结构的重要参数包括&#xff1a;氧化层厚度d&#xff1b;施加于金属平板上的电压V&#xff08;正偏压时V为正&#x…

pandas习题 071:字典元素列表构造 DataFrame

(编码题)以下有一个列表嵌套字典 data,列表中的每个字典 fields 中的列表为每行数据的值,另有一个 col 为列名,利用这两个数据构造一个 DataFrame。 data = [{fields: [2024-10-07T21:22:01, USER-A, 21, 0,

037 DFS回溯

1.回溯模板求排列 2.回溯模板求子集 # 当前位于点x&#xff0c;步长为length def dfs(x,length):passvis[x]length #接下来走下一个点 #判断下一个点是否走过if vis[a[x]]!0:#此时存在环global ansansmax(ans,length-vis[a[x]]1)else:dfs(a[x],length1)nint(input()) a[0]list(…

#systemverilog# Verilog与SystemVerilog发展历程及关系

1. Verilog的发展历史 1984年:Gateway Design Automation公司开发了Verilog,最初作为专有语言,用于逻辑仿真和数字电路设计。 1990年:Cadence收购Gateway,Verilog逐步开放,成为行业标准。 1995年(IEEE 1364-1995):首个IEEE标准,即Verilog-1995,定义基础语法和仿真语…

Kafka流式计算架构

引言 Kafka 凭借其卓越的架构设计&#xff0c;具备极为高效的流式计算能力&#xff0c;在海量数据环境下&#xff0c;依然能够以惊人的速度实现消息的高性能消费&#xff0c;轻松应对高并发、低延迟的严苛业务需求。无论是实时数据处理、复杂事件分析&#xff0c;还是大规模数…

RK3568使用QT搭建TCP服务器和客户端

文章目录 一、让RK3568开发板先连接上wifi二、客户端代码1. `widget.h` 文件2. `widget.cpp` 文件**详细讲解**1. **`Widget` 类构造函数 (`Widget::Widget`)**2. **UI 布局 (`setupUI`)**3. **连接按钮的槽函数 (`onConnectClicked`)**4. **发送消息按钮的槽函数 (`onSendMess…

解决 LeetCode 922 题:按奇偶排序数组 II

解决 LeetCode 922 题&#xff1a;按奇偶排序数组 II 题目描述 给定一个非负整数数组 nums&#xff0c;其中一半整数是奇数&#xff0c;一半整数是偶数。要求对数组进行排序&#xff0c;以便当 nums[i] 为奇数时&#xff0c;i 也是奇数&#xff1b;当 nums[i] 为偶数时&#…

redis教程

Redis 教程 Redis 是一个开源的内存数据结构存储系统&#xff0c;用作数据库、缓存和消息代理。以下是一些基础知识和常用操作。 一、简介 Redis 支持多种数据结构&#xff0c;如字符串、哈希、列表、集合、有序集合等。它具有高性能、高可用性和数据持久化的特性。 二、安…

力扣988. 从叶结点开始的最小字符串

Problem: 988. 从叶结点开始的最小字符串 文章目录 题目描述思路复杂度Code 题目描述 思路 遍历思想(利用二叉树的先序遍历) 在先序遍历的过程中&#xff0c;用一个变量path拼接记录下其组成的字符串&#xff0c;当遇到根节点时再将其反转并比较大小&#xff08;字典顺序大小&…

本地Ollama部署DeepSeek R1模型接入Word

目录 1.本地部署DeepSeek-R1模型 2.接入Word 3.效果演示 4.问题反馈 上一篇文章办公新利器&#xff1a;DeepSeekWord&#xff0c;让你的工作更高效-CSDN博客https://blog.csdn.net/qq_63708623/article/details/145418457?spm1001.2014.3001.5501https://blog.csdn.net/qq…

Codeforces Round 1002 (Div. 2)(部分题解)

补题链接 A. Milya and Two Arrays 思路&#xff1a;题意还是比较好理解&#xff0c;分析的话我加了一点猜的成分&#xff0c;对a&#xff0c;b数组的种类和相加小于4就不行&#xff0c;蒋老师的乘完后小于等于2也合理。 AC代码&#xff1a; #include <bits/stdc.h> u…