多缓存无锁极简实现

本文探讨了多缓存无锁实现的两种方案:双缓冲区和三缓冲区。双缓冲方案中,读写操作分别在两个缓冲区进行,通过交换保证数据一致性。然而,可能存在并发问题。为了提高安全性,提出了计数机制或采用三buffer方案,后者在读写操作间增设隔离缓冲,降低冲突概率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

双缓冲区方案

  • 双缓冲方案分配两段缓冲区:读(当前)缓冲和写(下一)缓冲。当执行查询操作时,总是读取当前缓冲区;当执行写操作时,总是在下一缓冲区上操作。当写操作执行完成后,交换操作会立刻将当前缓冲区和下一缓冲区交换,这样,最近更新的缓冲区便可供查询使用,旧的查询缓冲区用于写操作。
  • 示例代码:
template <typename T> 
class DoubleBuffer {
public:
    DoubleBuffer():flag(false){}
    T* write() {
        return flag ? &buffer1 : &buffer2;
    }
    T* read() {
        return flag ? &buffer2 : &buffer1;
    }
    void swap() {
        flag = !flag;
    }
private:
    bool flag;
    T buffer1;
    T buffer2;
};


template <typename T> 
class Updater {
public:
    T* reload_handler() {
        model.write()->clear();
        return model.write();
    }
    T* inc_handler() {
        *model.write() = *model.read();
        return model.write();
    }
    int done() {
        model.swap();
        return 0;
    }
    T* read() {
        return model.read();
    }
private:
    DoubleBuffer<T> model;
};

使用

#include <iostream>
#include <map>
#include "Updater.hpp"
int main(){
    Updater<std::map<int, int> > update_map;
    std::map<int, int> *p = update_map.reload_handler();
    p->insert(std::make_pair(1, 100));
    p->insert(std::make_pair(2, 200));
    update_map.reload_handler()->insert();
    update_map.done();
    // update_map中存有:1 100   2 200
    std::map<int, int> *pr = update_map.read();
    
    p = update_map.inc_handler();
    p->insert(std::make_pair(10, 100));
    p->insert(std::make_pair(20, 200));
    update_map.done();
    // update_map中存有:1 100   2 200   10 100    20 200
    pr = update_map.read();
}

安全性的考虑

1.在当前五分钟定时加载,每次加载时长两分钟左右,上述代码完全够用
2.代码依旧存在可能出现问题的情况,但是该可能微乎其微。
——当每次加载时长几乎和定时加载间隔相同时有可能出现如下问题——对正在读的缓冲执行写操作
1. 线程1中,更新操作w更新write_buffer
2. 线程2中,查询操作r指向read_buffer
3. 线程1中,更新操作w完成后执行交换
4. 线程2中ii步骤r操作持续,同时,线程1中新的更新操作开始。此时线程1和线程2会操作同一块内存
考虑到查询操作速度远高于更新操作,出现问题的的概率极低

改进方案

方案1: 计数

可以通过使用shared_ptr指针,看当前内存块引用数量是否仅为1,为1时再允许写操作,否则等待。

方案2: 三buffer方案

该方案增加一个缓冲区,分别称为只读buffer、只写buffer、读写隔离buffer
切换函数示例如下

void switch_buffer()
{
    *_cache_buffer = *_write_buffer;
    T* tmp = _write_buffer;
    _write_buffer = _cache_buffer;
    _cache_buffer = _read_buffer;
    _read_buffer = tmp;
}

以R、W、C分别表示只读buffer、只写buffer、读写隔离buffer;三块内存原始数据;r、w、c分别表示指向三块内存的指针;tmp为辅助指针;A、B、C三列为三个内存块,则上述流程可表示如下:
在这里插入图片描述
可以看出与双buffer相比,三buffer交换完成后将读指针切换到了最近更新的(写)内存,而旧的读内存块将持续存在一段时间,这一定程度保证了对旧内存块执行读操作的安全性。写操作则在第三块内存上执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值