【智鹿空间】c++实现了一个简单的链表数据结构 MyList,其中包含基本的 Get 和 Modify 操作,

这段代码实现了一个简单的链表数据结构 MyList,其中包含基本的 GetModify 操作,并且支持多线程访问。此外,还使用了 EBR (Epoch-Based Reclamation) 技术来管理内存回收,以及 benchmark 库来进行性能测试。以下是对代码的逐行注释:

cpp 复制代码
#include <benchmark/benchmark.h>  // 引入 Google Benchmark 库
#include <mutex>                  // 引入 C++ 标准库中的互斥锁
#include "ebr.h"                  // 引入 EBR (Epoch-Based Reclamation) 相关头文件
#include "spin_lock.h"            // 引入自旋锁相关头文件

// 定义链表节点结构
struct Node {
  Node() : lock(), next(nullptr) {}  // 构造函数,初始化锁和下一个指针
  int key;                           // 键值
  int value;                         // 值
  Node *next;                        // 指向下一个节点的指针
  SpinLock lock;                     // 自旋锁,用于保护节点
};

// 定义节点释放类,用于自动删除节点
class NodeFree {
 public:
  NodeFree(Node *node) { delete node; }  // 构造函数,删除传入的节点
};

// 定义链表类
class MyList {
 public:
  MyList() {
    Node *pre_node = nullptr;
    auto *&cur_node = root_;  // 当前节点指针
    for (int i = 0; i < 10; i++) {
      cur_node = new Node;     // 创建新节点
      cur_node->key = i;       // 设置键值
      cur_node->value = i;     // 设置值
      cur_node->next = pre_node;  // 设置下一个指针
      pre_node = cur_node;     // 更新前一个节点指针
    }
  }

  // 获取节点值
  int Get(int key, int *value) {
    root_->lock.lock();  // 锁住根节点
    auto *cur_node = root_->next;  // 当前节点指针
    auto *pre_node = root_;  // 前一个节点指针
    while (cur_node != nullptr) {
      cur_node->lock.lock();  // 锁住当前节点
      pre_node->lock.unlock();  // 解锁前一个节点
      if (key == cur_node->key) {  // 如果找到键值
        *value = cur_node->value;  // 设置返回值
        cur_node->lock.unlock();  // 解锁当前节点
        return 0;  // 返回成功
      }
      pre_node = cur_node;  // 更新前一个节点指针
      cur_node = cur_node->next;  // 移动到下一个节点
    }
    pre_node->lock.unlock();  // 解锁最后一个节点
    return 1;  // 返回失败
  }

  // 修改节点值
  int Modify(int key, int value) {
    root_->lock.lock();  // 锁住根节点
    auto *cur_node = root_->next;  // 当前节点指针
    auto *pre_node = root_;  // 前一个节点指针
    while (cur_node != nullptr) {
      cur_node->lock.lock();  // 锁住当前节点
      pre_node->lock.unlock();  // 解锁前一个节点
      if (key == cur_node->key) {  // 如果找到键值
        cur_node->value = value;  // 修改值
        cur_node->lock.unlock();  // 解锁当前节点
        return 0;  // 返回成功
      }
      pre_node = cur_node;  // 更新前一个节点指针
      cur_node = cur_node->next;  // 移动到下一个节点
    }
    pre_node->lock.unlock();  // 解锁最后一个节点
    return 1;  // 返回失败
  }

  // 使用 EBR 的获取节点值
  int GetUseEbr(int key, int *value) {
    ebr_mgr_.StartRead();  // 开始读操作
    auto *cur_node = root_->next;  // 当前节点指针
    while (cur_node != nullptr) {
      if (key == cur_node->key) {  // 如果找到键值
        *value = cur_node->value;  // 设置返回值
        ebr_mgr_.EndRead();  // 结束读操作
        return 0;  // 返回成功
      }
      cur_node = cur_node->next;  // 移动到下一个节点
    }
    ebr_mgr_.EndRead();  // 结束读操作
    return 1;  // 返回失败
  }

  // 使用 EBR 的修改节点值
  int ModifyUseEbr(int key, int value) {
    root_->lock.lock();  // 锁住根节点
    auto *cur_node = root_->next;  // 当前节点指针
    auto *pre_node = root_;  // 前一个节点指针
    while (cur_node != nullptr) {
      cur_node->lock.lock();  // 锁住当前节点
      if (key == cur_node->key) {  // 如果找到键值
        auto *new_node = new Node;  // 创建新节点
        new_node->key = cur_node->key;  // 设置键值
        new_node->value = value;  // 设置值
        new_node->next = cur_node->next;  // 设置下一个指针
        pre_node->next = new_node;  // 更新前一个节点的下一个指针
        cur_node->lock.unlock();  // 解锁当前节点
        pre_node->lock.unlock();  // 解锁前一个节点
        ebr_mgr_.FreeObject(cur_node);  // 释放旧节点
        return 0;  // 返回成功
      }
      auto *next_node = cur_node->next;  // 获取下一个节点
      pre_node->lock.unlock();  // 解锁前一个节点
      pre_node = cur_node;  // 更新前一个节点指针
      cur_node = next_node;  // 移动到下一个节点
    }
    pre_node->lock.unlock();  // 解锁最后一个节点
    return 1;  // 返回失败
  }

 private:
  Node *root_;  // 根节点指针
  EbrManager<Node, NodeFree, 15> ebr_mgr_;  // EBR 管理器
};

// 定义基准测试类
class MyBenchmark : public benchmark::Fixture {
 public:
  void SetUp(const ::benchmark::State &state) override {}  // 设置基准测试

  MyList &GetMyList() { return l; }  // 获取 MyList 实例

 private:
  MyList l;  // MyList 实例
  std::once_flag flag;  // 一次性标志
};

constexpr int kKeySize = 10000;  // 定义键值大小

// 定义不使用 EBR 的多线程工作基准测试
BENCHMARK_DEFINE_F(MyBenchmark, MultiThreadedWorkNoUseEbr)(benchmark::State &state) {
  for (auto _ : state) {
    auto &mylist = GetMyList();  // 获取 MyList 实例
    if (0 == state.thread_index()) {  // 主线程
      // 修改操作
      for (int i = 0; i < kKeySize; i++) {
        mylist.Modify(i % 9, i);  // 修改节点值
      }
    } else {  // 其他线程
      // 获取操作
      for (int i = 0; i < kKeySize; i++) {
        int value;
        mylist.Get(i % 9, &value);  // 获取节点值
      }
    }
  }
}

// 定义使用 EBR 的多线程工作基准测试
BENCHMARK_DEFINE_F(MyBenchmark, MultiThreadedWorkUseEbr)(benchmark::State &state) {
  for (auto _ : state) {
    auto &mylist = GetMyList();  // 获取 MyList 实例
    if (0 == state.thread_index()) {  // 主线程
      // 修改操作
      for (int i = 0; i < kKeySize; i++) {
        mylist.ModifyUseEbr(i % 9, i);  // 修改节点值
      }
    } else {  // 其他线程
      // 获取操作
      for (int i = 0; i < kKeySize; i++) {
        int value;
        mylist.GetUseEbr(i % 9, &value);  // 获取节点值
      }
    }
  }
}

// 注册基准测试,并指定线程数
BENCHMARK_REGISTER_F(MyBenchmark, MultiThreadedWorkNoUseEbr)->Threads(4);
BENCHMARK_REGISTER_F(MyBenchmark, MultiThreadedWorkNoUseEbr)->Threads(8);
BENCHMARK_REGISTER_F(MyBenchmark, MultiThreadedWorkNoUseEbr)->Threads(12);

BENCHMARK_REGISTER_F(MyBenchmark, MultiThreadedWorkUseEbr)->Threads(4);
BENCHMARK_REGISTER_F(MyBenchmark, MultiThreadedWorkUseEbr)->Threads(8);
BENCHMARK_REGISTER_F(MyBenchmark, MultiThreadedWorkUseEbr)->Threads(12);

BENCHMARK_MAIN();  // 运行基准测试

代码解释

  1. Node 结构体

    • 定义了一个链表节点,包含键值、值、下一个节点指针和自旋锁。
  2. NodeFree 类

    • 用于自动删除节点,构造函数中调用 delete 释放节点内存。
  3. MyList 类

    • 实现了一个简单的链表,支持 GetModify 操作。
    • GetModify 方法使用自旋锁来保护节点。
    • GetUseEbrModifyUseEbr 方法使用 EBR 技术来管理内存回收。
  4. MyBenchmark 类

    • 继承自 benchmark::Fixture,用于定义基准测试。
    • GetMyList 方法返回 MyList 实例。
  5. 基准测试定义

    • MultiThreadedWorkNoUseEbrMultiThreadedWorkUseEbr 分别定义了不使用 EBR 和使用 EBR 的多线程基准测试。
    • 主线程执行 Modify 操作,其他线程执行 Get 操作。
  6. 基准测试注册

    • 使用 BENCHMARK_REGISTER_F 注册基准测试,并指定不同线程数。
  7. 运行基准测试

    • BENCHMARK_MAIN 运行所有注册的基准测试。

通过这些注释,你应该能够更好地理解代码的结构和功能。如果有任何具体问题或需要进一步的帮助,请随时告知。

相关推荐
唐诺4 小时前
几种广泛使用的 C++ 编译器
c++·编译器
冷眼看人间恩怨5 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
菜鸡中的奋斗鸡→挣扎鸡5 小时前
滑动窗口 + 算法复习
数据结构·算法
红龙创客5 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin5 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
axxy20006 小时前
leetcode之hot100---240搜索二维矩阵II(C++)
数据结构·算法
yuanbenshidiaos6 小时前
c++---------数据类型
java·jvm·c++
十年一梦实验室7 小时前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
taoyong0017 小时前
代码随想录算法训练营第十一天-239.滑动窗口最大值
c++·算法
这是我587 小时前
C++打小怪游戏
c++·其他·游戏·visual studio·小怪·大型·怪物