双数组加原子索引实现数据更新和访问¶
#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;
};