当前位置: 首页 > news >正文

【中间件】bthread_数据结构_学习笔记

bthread数据结构

  • bthread_数据结构_学习笔记
    • 1 pthread_cond_t
      • 1.1 definition
      • 1.2 解释
      • 1.3 设计动机
      • 1.4 使用示例
      • 1.5 注意事项
      • 1.6 进一步延伸:pthread_cond_s
    • 2 pthread_mutex_t

bthread_数据结构_学习笔记

1 pthread_cond_t

POSIX线程库 /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h
底层通过union实现,系统编程中“隐藏实现细节,暴露稳定接口”的经典范例。

主要目的是为了兼容不同平台的底层实现差异,同时确保类型的内存布局和对齐方式符合规范。

  • 类型抽象:隐藏平台差异,提供统一接口。
  • 内存安全:固定大小和强制对齐避免常见错误。
  • 灵活初始化:支持静态和动态初始化方式。

1.1 definition

typedef union {struct __pthread_cond_s __data;   // 实际存储条件变量数据的结构体char __size[__SIZEOF_PTHREAD_COND_T]; // 保证联合体大小与平台实现一致__extension__ long long int __align;  // 强制对齐(通常是 8 字节对齐)
} pthread_cond_t;

1.2 解释

  1. struct __pthread_cond_s __data

    • 包含条件变量的实际数据(如等待队列、计数器等),具体字段由底层实现定义。
    • 开发者通常不直接操作此结构体,而是通过 pthread_cond_* 系列函数(如 pthread_cond_init, pthread_cond_wait)间接使用。
  2. char __size[__SIZEOF_PTHREAD_COND_T]

    • 用于确保联合体的总大小与平台实现的 pthread_cond_t 一致。
    • __SIZEOF_PTHREAD_COND_T 是一个宏,表示目标平台上 pthread_cond_t 的字节数,由编译器或系统头文件提供。
  3. long long int __align

    • 强制联合体按 long long(通常 8 字节)对齐,避免内存对齐问题。
    • __extension__ 是 GCC 扩展语法,用于忽略编译器的严格标准检查。

1.3 设计动机

  1. 跨平台兼容性

    • 不同操作系统或硬件架构对 pthread_cond_t 的实现可能不同(如字段顺序、对齐要求)。
    • 通过联合体将实现细节隐藏在 __data 结构体中,用户只需使用 pthread_cond_t 类型,无需关心底层差异。
  2. 固定内存布局

    • __size 数组确保联合体的大小严格等于 __SIZEOF_PTHREAD_COND_T,避免用户误分配不足内存。
    • 例如,静态初始化条件变量时,用户可以直接用 PTHREAD_COND_INITIALIZER 宏,而无需知道具体结构。
  3. 对齐控制

    • __align 成员确保联合体按最大对齐要求(如 8 字节)分配内存,防止因对齐不当导致的性能问题或硬件异常。

1.4 使用示例

// 静态初始化(依赖联合体的大小和对齐)
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;// 动态初始化
pthread_cond_t cond;
pthread_cond_init(&cond, NULL);// 使用条件变量
pthread_cond_wait(&cond, &mutex);

1.5 注意事项

  • 不要直接操作 __data 成员
    条件变量的内部结构是平台相关的,直接访问可能导致不可移植或未定义行为。

  • 依赖标准函数
    始终使用 pthread_cond_* 函数操作条件变量,而非手动修改联合体成员。

  • 内存对齐
    联合体的对齐机制确保了跨平台安全性,但用户仍需避免将 pthread_cond_t 放置在不对齐的内存地址。


1.6 进一步延伸:pthread_cond_s

Linux 系统中 POSIX 条件变量 pthread_cond_t 的底层实现。是 Linux 高效线程同步的核心,通过原子计数器、分组策略和引用机制,平衡了性能与正确性。理解其字段有助于:

  • 调试复杂的线程竞争问题。
  • 编写高性能的多线程代码(如避免不必要的广播)。
  • 深入操作系统级并发原语的实现原理。

它的字段设计用于高效管理线程的等待与唤醒机制,同时确保线程安全和性能。

definition

struct __pthread_cond_s {__atomic_wide_counter __wseq;     // 全局等待序列号(原子计数器)__atomic_wide_counter __g1_start; // 第一组(Group 1)的起始序列号(原子计数器)unsigned int __g_refs[2];  __LOCK_ALIGNMENT       // 组的引用计数(每个组对应一个槽)unsigned int __g_size[2];         // 组的大小(跟踪每个组的等待线程数)unsigned int __g1_orig_size;      // Group 1 的原始大小(用于广播操作)unsigned int __wrefs;             // 等待者的引用计数(防止销毁时竞争)unsigned int __g_signals[2];      // 组的待处理信号数(唤醒信号计数)
} __LOCK_ALIGNMENT;                   // 结构体对齐(通常与锁对齐一致)

字段详细说明

  1. __atomic_wide_counter __wseq

    • 作用:全局等待序列号,原子计数器。
    • 细节
      • 每个线程在调用 pthread_cond_wait 进入等待前会递增此值,表示一次新的等待事件。
      • 用于唯一标识等待操作的顺序,避免唤醒操作(如 pthread_cond_signalpthread_cond_broadcast)遗漏或重复。
  2. __atomic_wide_counter __g1_start

    • 作用:第一组(Group 1)的起始等待序列号。
    • 细节
      • 条件变量将等待线程划分为两个组(Group 1 和 Group 2),以减少竞争。
      • __g1_start 记录 Group 1 的起始序列号,当 Group 1 的线程全部被唤醒后,Group 2 会晋升为新的 Group 1。
  3. unsigned int __g_refs[2]

    • 作用:每个组的引用计数,用于跟踪活跃的等待线程数。
    • 细节
      • 索引 0 对应 Group 1,索引 1 对应 Group 2。
      • 线程进入等待时会增加对应组的引用计数,唤醒后减少。
      • 防止在唤醒操作过程中组被错误回收。
  4. unsigned int __g_size[2]

    • 作用:每个组的当前等待线程数。
    • 细节
      • __g_refs 配合使用,确保唤醒操作(如 pthread_cond_broadcast)能正确统计需要唤醒的线程数量。
      • 例如,pthread_cond_broadcast 会唤醒某个组的所有线程,__g_size 用于确定唤醒的数量。
  5. unsigned int __g1_orig_size

    • 作用:Group 1 的原始大小,用于广播操作。
    • 细节
      • 在调用 pthread_cond_broadcast 时,会记录 Group 1 的初始大小,确保所有在广播前进入等待的线程都被唤醒。
      • 避免广播过程中新加入的线程被错误唤醒。
  6. unsigned int __wrefs

    • 作用:等待者的引用计数。
    • 细节
      • 跟踪当前正在等待的线程总数(包括所有组)。
      • 在销毁条件变量(pthread_cond_destroy)时,需确保 __wrefs 为 0,防止有线程仍在等待时销毁资源。
  7. unsigned int __g_signals[2]

    • 作用:每个组的待处理唤醒信号数。
    • 细节
      • 当调用 pthread_cond_signalpthread_cond_broadcast 时,信号会被记录到对应组的 __g_signals 中。
      • 等待线程被唤醒前会检查此值,确保每个信号只唤醒一个线程(避免“惊群效应”)。
  8. __LOCK_ALIGNMENT

    • 作用:结构体对齐标记。

    • 细节

      • 通常与系统锁(如 pthread_mutex_t)的对齐方式一致,确保条件变量和互斥锁在内存中正确对齐,避免性能下降或硬件异常。
      • 处理器访问内存时,若数据的地址是某个值的整数倍(如4字节对齐的地址为4的倍数),则访问效率更高。
      • 未对齐的访问可能导致:
        • 性能下降:处理器可能需要多次内存操作来读取或写入未对齐的数据。
        • 硬件异常:在某些架构(如ARM或SPARC)上,未对齐访问会直接触发错误。
        • 原子操作失败:同步原语(如锁)依赖原子指令,未对齐的结构体可能导致指令无法正确执行。
    • 平台兼容性

      • 不同硬件架构(如x86、ARM、RISC-V)的对齐要求可能不同。例如:
        • x86 允许未对齐访问(但性能较低)。
        • ARMv7 要求严格对齐,否则触发总线错误。
    • __LOCK_ALIGNMENT 通过宏定义适配目标平台的对齐要求,例如:#define __LOCK_ALIGNMENT __attribute__((aligned(8))) // 8字节对齐确保结构体在所有平台上均按硬件要求对齐。

    • 优化内存访问

      • 对齐后的结构体字段排列更紧凑,减少填充(Padding)字节,节省内存。
      • 对齐的字段可被处理器单次内存操作访问,提升指令执行效率。
    • 支持原子操作

      • 条件变量的实现依赖原子计数器(如 __atomic_wide_counter)。
      • 原子操作指令(如CAS, LL/SC)通常要求操作数对齐。未对齐的原子操作可能失败或不可用。
    • __LOCK_ALIGNMENT 确保原子字段(如 __wseq、__g1_start)按原子指令的要求对齐。

    • 作用归纳:

      • 兼容性:适配不同硬件平台的对齐要求。
      • 性能优化:减少伪共享、提升内存访问效率。
      • 正确性:确保原子操作和同步机制可靠工作。
      • 协作安全:与互斥锁对齐一致,避免协同使用时的潜在问题。
    • 系统级编程中“显式控制内存布局”的典范,确保了多线程同步原语的高效与稳定.

组(Group)机制
条件变量通过 分组策略 优化唤醒操作:

  1. Group 1 和 Group 2
    • Group 1 是当前活跃的等待组,新线程在等待时加入 Group 1。
    • 当 Group 1 的线程被全部唤醒后,Group 2 晋升为新的 Group 1,避免操作同一组时的竞争。
  2. 广播(Broadcast)优化
    • pthread_cond_broadcast 会唤醒 Group 1 的所有线程,同时记录 __g1_orig_size 确保只唤醒广播前的等待线程。
  3. 信号分发
    • pthread_cond_signal 优先唤醒 Group 1 的线程,若 Group 1 无等待线程,则唤醒 Group 2。

关键操作流程

  1. 等待操作(pthread_cond_wait

    • 线程递增 __wseq 进入等待队列。
    • 根据当前组策略(Group 1 或 Group 2)更新 __g_refs__g_size
    • 检查 __g_signals,若无待处理信号,则阻塞线程。
  2. 唤醒单个线程(pthread_cond_signal

    • 检查 Group 1 的 __g_size,若有等待线程,递增 __g_signals[0] 并唤醒一个线程。
    • 若 Group 1 无等待线程,则对 Group 2 执行相同操作。
  3. 唤醒所有线程(pthread_cond_broadcast

    • 记录 __g1_orig_size 为当前 Group 1 的大小。
    • __g_signals[0] 设为 __g1_orig_size,唤醒所有 Group 1 的线程。
    • Group 1 的线程唤醒后,__g_refs[0]__g_size[0] 被清零,Group 2 晋升为新 Group 1。

优势

  1. 减少锁竞争
    • 通过分组机制将唤醒操作分散到不同组,降低多核环境下的锁争用。
  2. 避免信号丢失
    • 原子计数器和序列号确保每个等待线程都能被正确追踪。
  3. 高效广播
    • __g1_orig_size__g_signals 的配合使广播操作无需遍历队列,直接通过计数器批量唤醒。
  4. 内存安全
    • __wrefs 防止条件变量在销毁时仍有线程等待。

示例场景
假设一个生产者-消费者模型:

  • 消费者线程 调用 pthread_cond_wait 进入等待,加入 Group 1,递增 __wseq__g_refs[0]__g_size[0]
  • 生产者线程 调用 pthread_cond_signal,发现 Group 1 的 __g_size[0] > 0,递增 __g_signals[0] 并唤醒一个消费者。
  • 唤醒的消费者递减 __g_refs[0]__g_size[0],继续执行任务。

2 pthread_mutex_t

相关文章:

  • terraform 删除资源前先校验资源是否存在关联资源
  • AJAX 实例
  • 【Linux】线程池和线程补充内容
  • Qwen3 正式发布
  • C++——入门基础(2)
  • 工 厂 模 式
  • 游戏引擎学习第252天:允许编辑调试值
  • 企业内训|智能驾驶与智能座舱技术——某汽车厂商
  • 【Qt】网络
  • Python项目源码69:Excel数据筛选器1.0(tkinter+sqlite3+pandas)
  • 《数据结构初阶》【顺序表/链表 精选15道OJ练习】
  • 【数据结构】- 栈
  • 文件操作--文件包含漏洞
  • 如何让Steam下载速度解除封印?!
  • PyTorch线性代数操作详解:点积、矩阵乘法、范数与轴求和
  • 字符串转换整数(atoi)(8)
  • 在阿里云 Ubuntu 24.04 上部署 RabbitMQ:一篇实战指南
  • 【进阶】--函数栈帧的创建和销毁详解
  • Spring MVC 与 FreeMarker 整合
  • OpenGL-ES 学习(10) ---- OpenGL-ES Shader语言语法
  • 安徽两位新任地级市政府党组书记亮相
  • 申活观察|咖香涌动北外滩,带来哪些消费新想象?
  • 空间站第八批科学实验样品返抵地球并交付科学家
  • 美国务院宣布新一轮与伊朗相关的制裁
  • “乐购浦东”消费券明起发放,多个商家同期推出折扣促销活动
  • 中国银行副行长刘进任该行党委副书记