借shared_ptr实现写时复制(COW)

发布时间 2023-12-25 17:10:58作者: HOracle

原理

1、使用智能指针管理共享资源

2、write端,若引用计数为1,则write端独占资源,若引用计数不为1,则对共享资源备份进行写操作,以确保线程安全

3、read端,读之前引用计数加1,write端此时若并发访问共享资源,则会发现引用计数不为1,write端不会直接写共享资源,确保线程安全

代码

#include <memory>
#include <vector>
#include <mutex>

// 共享资源
std::shared_ptr<std::vector<int>> g_vptr;

// 该互斥锁只用来保证访问shared_ptr时的线程安全
// 读写std::vector<int>的线程安全通过shared_ptr的引用计数保证
std::mutex g_mutex;

void read()
{
    std::shared_ptr<std::vector<int>> vptr;
    {
        std::lock_guard<std::mutex> g(g_mutex);
        vptr = g_vptr;
    }

    for(auto i : *vptr)
    {
        // read
    }
}

void write(int data)
{
    std::lock_guard<std::mutex> g(g_mutex);
    if(!g_vptr.unique())
    {
        g_vptr.reset(new std::vector<int>(*g_vptr));
    }

    g_vptr->push_back(data);
}

代码释义

假设read时开始了并发writeread后原对象引用计数为2,即vptrg_vptr都指向原对象,write时对g_vptr进行reset操作后,原对象引用计数减1,此时原对象引用计数为1,还不会被析构,只有vptr指向原对象,而g_vptr指向原对象的副本(一个新的对象),这样以来write里对g_vptr的写和read里对vptr的读不会相互影响。当read里完成对vptr的读,原对象引用计数减1,原对象引用计数为0,原对象析构,而write里被修改的原对象副本此时已经成为新的原对象,后续读写都将基于这个新的原对象进行

参考

陈硕《Linux多线程服务端编程》