【问题标题】:c++ how to manage object cache with ref countingc ++如何使用引用计数管理对象缓存
【发布时间】:2013-01-26 12:35:12
【问题描述】:

我的程序将创建和删除大量对象(来自 REST API)。这些对象将被多个地方引用。我想要一个“内存缓存”并通过引用计数来管理对象的生命周期,以便在不再使用它们时释放它们。

所有对象都继承自基类Ressource

Cache 主要是 std::map<_key_, std::shared_ptr<Ressource> >

那我就纳闷了,Cache怎么知道Ressource的引用计数什么时候减少了? IE。致电std::shared_ptr destructoroperator=

1/ 我不想遍历 std::map 并检查每个 ref.count()。

2/ 我可以重用 std::shared_ptr 并实现自定义钩子吗?

class RessourcePtr : public std::shared_ptr<Ressource>
...

3/ 我应该实现自己的引用计数类吗?前任。 https://stackoverflow.com/a/4910158/1058117

谢谢!

【问题讨论】:

  • 那将是一个奇怪的缓存。想象一下,我请求一个资源,我的指针超出范围,然后再次请求它。它会触发对资源的新请求,实际上不会发生缓存。您可能想了解一些著名的缓存算法。
  • @pmr :哦,这取决于。缓存的资源可以立即释放,也可以不释放。这背后的问题是确保对象与服务器保持同步。

标签: c++ shared-ptr smart-pointers


【解决方案1】:

make shared_ptr not use delete 展示了如何为共享指针提供自定义删除功能。

如果您想要使用客户函数进行引用添加和删除,也可以使用侵入式指针。

【讨论】:

  • 哇。这看起来很有趣!我一定会检查这个,谢谢!
【解决方案2】:

您可以在字典中使用map&lt;Key, weak_ptr&lt;Resource&gt; &gt;

大概是这样工作的:

map<Key, weak_ptr<Resource> > _cache;

shared_ptr<Resource> Get(const Key& key)
{
    auto& wp = _cache[key];
    shared_ptr<Resource> sp; // need to be outside of the "if" scope to avoid
                             // releasing the resource
    if (wp.expired()) {
        sp = Load(key); // actually creates the resource
        wp = sp;
    }

    return wp.lock();
}

Get返回的所有shared_ptr都被销毁时,对象将被释放。缺点是如果你使用一个对象然后立即销毁共享指针,那么你并没有真正使用缓存,正如@pmr 在他的评论中所建议的那样。

编辑:您可能知道,此解决方案不是线程安全的,您需要锁定对 map 对象的访问。

【讨论】:

  • 有趣,谢谢 J.N.是的,它会立即破坏共享指针,你知道有什么方法可以对此进行更多控制吗?
  • 并非如此,通常的缓存会使对象保持活动状态,直到缓存已满和/或对象有一段时间未被访问。如果您计划在另一个线程中使用该资源,该线程将保留一个(或多个)副本 shared_ptr,这可能会很有用。
【解决方案3】:

问题是,在您的场景中,池将保持每个引用都处于活动状态。这是一个从引用计数为 1 的池中删除资源的解决方案。问题是,何时修剪池。此解决方案将在每次调用 get 时进行修剪。这样,“再次发布并获取”之类的场景会很快。

#include <memory>
#include <map>
#include <string>
#include <iostream>

struct resource {

};

class pool {
public:
  std::shared_ptr<resource> get(const std::string& x) 
  {
    auto it = cache_.find(x);
    std::shared_ptr<resource> ret;

    if(it == end(cache_))
      ret = cache_[x] = std::make_shared<resource>();
    else {
      ret = it->second;
    }
    prune();

    return ret;
  }

  std::size_t prune() 
  {
    std::size_t count = 0;
    for(auto it = begin(cache_); it != end(cache_);)
    {
      if(it->second.use_count() == 1) {
        cache_.erase(it++);
        ++count;
      } else {
        ++it;
      }
    }
    return count;
  }

  std::size_t size() const { return cache_.size(); }

private:
  std::map<std::string, std::shared_ptr<resource>> cache_;
};

int main()
{
  pool c;
  {
    auto fb = c.get("foobar");
    auto fb2 = c.get("foobar");
    std::cout << fb.use_count() << std::endl;
    std::cout << "pool size: " << c.size() << std::endl;
  }
  auto fb3 = c.get("bar");
  std::cout << fb3.use_count() << std::endl;
  std::cout << "pool size: " << c.size() << std::endl;
  return 0;
}

【讨论】:

    【解决方案4】:

    你不想要一个缓存你想要一个池。特别是对象池。您的主要问题不是如何实现引用计数,shared_ptr 已经为您做到了。当不再需要某个资源时,您只需将其从缓存中删除即可。您的主要问题将是由于不断分配/删除导致的内存碎片以及由于全局内存分配器中的争用而导致的缓慢。查看线程特定的内存池实现以获得答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-13
      • 1970-01-01
      • 2015-09-01
      • 2012-05-14
      • 1970-01-01
      • 2016-03-29
      • 2011-01-28
      相关资源
      最近更新 更多