跳转至

双数组加原子索引实现数据更新和访问

#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>

template <typename T>
class DoubleBuffer {
 public:
  // 初始化双缓冲区,使用两个数组
  DoubleBuffer(size_t size) : currentIndex(0) {
    buffers[0] = std::vector<T>(size);
    buffers[1] = std::vector<T>(size);
  }

  // 更新数据(生产者)
  void update(const std::vector<T>& newData) {
    // 获取下一个缓冲区的索引
    size_t nextIndex = 1 - currentIndex.load(std::memory_order_relaxed);

    // 更新下一个缓冲区的数据
    std::copy(newData.begin(), newData.end(), buffers[nextIndex].begin());

    // 使用内存屏障确保数据写入完成后才更新索引
    std::atomic_thread_fence(std::memory_order_release);

    // 原子地更新当前索引,让读取者可以访问新数据
    currentIndex.store(nextIndex, std::memory_order_relaxed);
  }

  // 访问当前数据(消费者)
  std::vector<T> read() const {
    // 原子地获取当前索引
    size_t index = currentIndex.load(std::memory_order_relaxed);

    // 使用内存屏障确保索引读取完成后才读取数据
    std::atomic_thread_fence(std::memory_order_acquire);

    // 返回当前缓冲区的副本
    return buffers[index];
  }

 private:
  std::vector<T> buffers[2];         // 两个数据缓冲区
  std::atomic<size_t> currentIndex;  // 原子索引,指向当前活动的缓冲区
};

// 示例用法
int main() {
  DoubleBuffer<int> buffer(10);

  // 生产者线程:定期更新数据
  std::thread producer([&]() {
    for (int i = 0; i < 5; ++i) {
      std::vector<int> newData(10);
      for (size_t j = 0; j < newData.size(); ++j) {
        newData[j] = i * 10 + j;
      }

      buffer.update(newData);
      std::cout << "Producer updated data: iteration " << i << std::endl;

      // 模拟生产延迟
      std::this_thread::sleep_for(std::chrono::seconds(1));
    }
  });

  // 消费者线程:定期读取数据
  std::thread consumer([&]() {
    for (int i = 0; i < 8; ++i) {
      auto data = buffer.read();
      std::cout << "Consumer read data: ";
      for (int value : data) {
        std::cout << value << " ";
      }
      std::cout << std::endl;

      // 模拟消费延迟
      std::this_thread::sleep_for(std::chrono::milliseconds(700));
    }
  });

  producer.join();
  consumer.join();

  return 0;
}

双数组同时能结合干跑模式,在无锁化保证数据一致性的同时,通过缓存预热降低热路径延迟。

#include <algorithm>
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>

template <typename T>
class DoubleBufferWithDryRun {
 public:
  // 初始化:双数组+原子索引,支持指定缓存行大小(默认64字节,适配Intel CPU)
  DoubleBufferWithDryRun(size_t size, size_t cacheLineSize = 64)
      : currentIndex(0), cacheLineSize(cacheLineSize) {
    buffers[0] = std::vector<T>(size);
    buffers[1] = std::vector<T>(size);
    // 初始化时执行一次干跑预热,加载初始数据到缓存
    dryRunWarmCache();
  }

  // 生产者更新数据
  void update(const std::vector<T>& newData) {
    size_t nextIndex = 1 - currentIndex.load(std::memory_order_relaxed);
    std::copy(newData.begin(), newData.end(), buffers[nextIndex].begin());
    std::atomic_thread_fence(std::memory_order_release);
    currentIndex.store(nextIndex, std::memory_order_relaxed);
    dryRunWarmCache();
  }

  // 消费者读取数据
  std::vector<T> read() const {
    size_t index = currentIndex.load(std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_acquire);
    return buffers[index];
  }

  // 干跑模式预热缓存
  void dryRunWarmCache() const {
    size_t activeIndex = currentIndex.load(std::memory_order_relaxed);
    size_t standbyIndex = 1 - activeIndex;

    size_t step = cacheLineSize / sizeof(T);
    if (step == 0) step = 1;

    for (size_t i = 0; i < buffers[activeIndex].size(); i += step) {
      volatile T temp =
          buffers[activeIndex][i];  // volatile禁止编译器优化,强制加载缓存
      (void)temp;
    }

    for (size_t i = 0; i < buffers[standbyIndex].size(); i += step) {
      volatile T temp = buffers[standbyIndex][i];
      (void)temp;
    }

    std::cout << "[Dry-Run] Cache warmed for both buffers (active: "
              << activeIndex << ", standby: " << standbyIndex << ")"
              << std::endl;
  }

 private:
  std::vector<T> buffers[2];
  std::atomic<size_t> currentIndex;
  const size_t cacheLineSize;
};