【中间件】brpc_基础_无锁id管理链表

文章目录

  • 无锁ID管理链表
    • 1 简介
    • 2 设计实现说明
    • 3 关键数据结构与实现
      • 3.1 链表节点结构
      • 3.2 空闲链表管理
      • 3.3 防 ABA 机制
    • 4 核心 API 与功能
      • 4.1 ID 分配
      • 4.2 ID 回收
      • 4.3 链表扩展
    • 5 性能优化与特性
    • 6 应用场景
    • 7 潜在问题与注意事项
    • 8 示例代码片段
    • 9 总结

无锁ID管理链表

头文件:src/bthread/list_of_abafree_id.h

1 简介

BRPC 中用于实现 无锁(Lock-Free)且避免 ABA 问题的 ID 管理链表 的核心组件,主要用于在多线程高并发场景下高效、安全地分配和回收唯一标识符(如任务 ID、资源句 柄等 )。

2 设计实现说明

设计目标:提供一种无需锁竞争、无内存泄漏且防 ABA 问题的轻量级 ID 管理机制,支撑 BRPC 的高性能线程调度和资源管理。

核心设计思想:

  1. ABA 问题规避
    通过 标签指针(Tagged Pointer)版本号(Versioning) 扩展指针的语义,确保每次修改链表节点时,指针的标签/版本号递增,从而避免 ABA 问题(即一个指针被释放后重新分配,但其值仍与旧值相同导致误判)。

    • 实现示例:使用 std::atomic 将指针的低位存储地址,高位存储标签(如 16 位地址 + 48 位标签)。
  2. 无锁链表操作
    使用原子操作(如 compare_exchange_weak)实现链表的插入、删除和遍历,确保线程安全且无阻塞。

  3. ID 复用管理
    维护一个空闲 ID 链表,支持快速分配和回收 ID,避免频繁的内存分配/释放操作。


3 关键数据结构与实现

3.1 链表节点结构

struct Node {
    uint64_t id;           // 唯一标识符(可能包含版本号)
    std::atomic<Node*> next; // 带标签的原子指针
};

3.2 空闲链表管理

  • 头指针std::atomic _free_list 指向空闲链表的头部,通过原子操作实现并发访问。
  • 分配 ID:从头节点取出一个 ID,并更新头指针。
  • 回收 ID:将 ID 插回链表头部,并递增标签防止 ABA。

3.3 防 ABA 机制

  • 标签指针:每次修改链表指针时递增标签值,确保即使地址复用,标签值不同也会导致 CAS 失败。
    bool try_push(Node* new_node) {
        Node* old_head = _free_list.load();
        new_node->next.store(old_head);
        return _free_list.compare_exchange_weak(old_head, new_node);
    }
    

4 核心 API 与功能

4.1 ID 分配

uint64_t allocate_id() {
    Node* old_head = _free_list.load();
    while (true) {
        if (old_head == nullptr) {
            return create_new_id(); // 扩展新 ID
        }
        Node* new_head = old_head->next.load();
        if (_free_list.compare_exchange_weak(old_head, new_head)) {
            return old_head->id; // 返回复用 ID
        }
    }
}

4.2 ID 回收

void release_id(uint64_t id) {
    Node* node = reinterpret_cast<Node*>(id_to_node(id));
    Node* old_head = _free_list.load();
    do {
        node->next.store(old_head);
    } while (!_free_list.compare_exchange_weak(old_head, node));
}

4.3 链表扩展

void expand_free_list(size_t n) {
    for (size_t i = 0; i < n; ++i) {
        Node* node = new Node{generate_new_id(), nullptr};
        release_id(node->id); // 将新生成的 ID 加入空闲链表
    }
}

5 性能优化与特性

  1. 内存池化
    预分配节点内存池,减少动态内存分配开销。
  2. 批量操作
    支持批量分配和回收 ID,减少原子操作频率。
  3. 无锁设计
    完全基于原子操作,避免锁竞争,适用于高并发场景。
  4. 标签溢出处理
    标签值达到上限时重置或扩展位数,防止回绕问题。

6 应用场景

  1. 任务 ID 管理
    为每个 bthread 分配唯一 ID,用于调度和状态跟踪。
  2. 资源句柄池
    管理网络连接、内存块等资源,支持快速分配和释放。
  3. 无锁队列索引
    作为无锁队列的槽位索引,避免 ABA 导致的数据错误。

7 潜在问题与注意事项

  1. 标签位数限制
    若标签位数不足(如 16 位),在高频回收场景下可能快速溢出,需合理设计标签长度。
  2. 内存对齐
    标签指针需确保地址对齐,避免低位被占用(如 x86_64 通常要求 16 字节对齐)。
  3. 跨平台兼容性
    不同平台对原子操作的实现可能不同,需适配(如 ARM 需内存屏障指令)。

8 示例代码片段

// 初始化空闲链表
std::atomic<Node*> _free_list{nullptr};

// 分配 ID
uint64_t id = allocate_id();

// 使用 ID
process_task(id);

// 回收 ID
release_id(id);

9 总结

list_of_abafree_id.h 提供了一种高效、无锁且防 ABA 的 ID 管理机制,是 BRPC 高并发能力的基石之一。其通过标签指针和原子操作实现了线程安全的 ID 分配与回收,适用于需要频繁创建和销毁资源的场景。开发者需关注标签溢出和平台兼容性,结合具体需求调整预分配策略和标签位数,以优化性能和稳定性。

你可能感兴趣的:(中间件,C/C++,中间件,链表,数据结构)