c++ - 无阻塞更新缓存

标签 c++ multithreading c++11 shared-ptr

我目前有一个程序具有类似缓存的机制。我有一个线程监听从另一台服务器到这个缓存的更新。该线程将在收到更新时更新缓存。这是一些伪代码:

void cache::update_cache()
{
    cache_ = new std::map<std::string, value>();
    while(true)
    {
        if(recv().compare("update") == 0)
        {
            std::map<std::string, value> *new_info = new std::map<std::string, value>();
            std::map<std::string, value> *tmp;
            //Get new info, store in new_info
            tmp = cache_;
            cache_ = new_cache;
            delete tmp;              
        }
    }
}

std::map<std::string, value> *cache::get_cache()
{
    return cache_;
}

cache_ 正在从许多不同的线程同时读取。我相信如果我的线程之一调用 get_cache(),然后我的缓存更新,然后该线程尝试访问存储的缓存,我会遇到未定义的行为。

我正在寻找一种方法来避免这个问题。我知道我可以使用互斥体,但我宁愿不阻止读取发生,因为它们必须尽可能低延迟,但如果需要,我可以采用这种方式。

我想知道这是否是 unique_ptr 的一个很好的用例。我的理解是否正确,因为如果一个线程调用 get_cache,并且返回一个 unique_ptr 而不是标准指针,一旦所有具有旧版本缓存的线程都完成了它(即离开范围),该对象将被删除。

对于这种情况,使用 unique_ptr 是最好的选择,还是有其他我没有想到的选择?

任何输入将不胜感激。

编辑:

我相信我在 OP 中犯了一个错误。我的意思是使用并传递一个 shared_ptr 而不是 cache_ 的 unique_ptr。当所有线程都使用完 cache_ 时,shared_ptr 应该自行删除。

关于我的程序的一点点:我的程序是一个网络服务器,它将使用这些信息来决定返回什么信息。这是相当高的吞吐量(数千个请求/秒)每个请求查询缓存一次,所以告诉我的其他线程何时更新是没有问题的。我可以容忍稍微过时的信息,如果可能的话,我更愿意阻止我所有的线程执行。缓存中的信息相当大,因此我想限制任何拷贝的值(value)。

update_cache 只运行一次。它在一个线程中运行,该线程仅监听更新命令并运行代码。

最佳答案

我觉得有很多问题:

1) 不要泄漏内存:为此永远不要在代码中使用“delete”并坚持使用 unique_ptr(或在特定情况下使用 shared_ptr)

2) 保护对共享数据的访问,使用锁定(互斥)或无锁机制(std::atomic)

class Cache {
    using Map = std::map<std::string, value>();
    std::unique_ptr<Map> m_cache;
    std::mutex m_cacheLock;
public:

    void update_cache()
    {
        while(true)
        {
            if(recv().compare("update") == 0)
            {
                std::unique_ptr<Map> new_info { new Map };
                //Get new info, store in new_info
                {
                   std::lock_guard<std::mutex> lock{m_cacheLock};
                   using std::swap;
                   swap(m_cache, new_cache);
                }
            }
        }
    }

注意:我不喜欢 update_cache() 成为缓存公共(public)接口(interface)的一部分,因为它包含一个无限循环。我可能会用 recv 外部化循环并有一个:

    void update_cache(std::unique_ptr<Map> new_info)
    {
        { // This inner brace is not useless, we don't need to keep the lock during deletion
           std::lock_guard<std::mutex> lock{m_cacheLock};
           using std::swap;
           swap(m_cache, new_cache);
        }
    }

现在对于缓存的读取,使用适当的封装并且不要将指针指向成员映射转义:

    value get(const std::string &key)
    {
        // lock, fetch, and return. 
        // Depending on value type, you might want to allocate memory
        // before locking
    }

如果缓存中不存在该值,则使用此签名必须抛出异常,另一种选择是返回类似 boost::optional 的内容。

总的来说,如果您在锁定部分之外进行代价高昂的操作(例如内存分配),您可以保持较低的延迟(一切都是相对的,我不知道您的用例)。

关于c++ - 无阻塞更新缓存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22647222/

相关文章:

java - ExecutorService,如何等待所有任务完成

windows - 搁置线程是最优的吗?

c++ - 从模板基类继承构造函数而不重复模板参数?

c++ - constexpr 变量必须由 const 表达式初始化

c++ - gcc:错误:在 MacOS 上构建 openjdk 9 时无法识别的命令行选项 '-mstack-alignment=16'

c++ - C++11 增强后的双端队列与 vector 引导

c++ - 用于创建用户友好的命令行提示符的工具?

iphone - 在iPhone上实现警报的最有效方法

c++ - 什么是 "hint"?

c++ - libstdc++ 是否实现 C++11 双端队列接口(interface)?