如何使用std::shared_ptr实现缓存管理器
How to implement cache manager using std::shared_ptr?
#include <mutex>
#include <assert.h>
#include <iostream>
#include <unordered_map>
#include <memory>
#include <string>
#include <stdio.h>
//
// Requirements:
// 1: the bitmap could be used by multiple thread safely.(std::shared_ptr could?)
// 2: cache the bitmap and do not always increase memeory
//@NotThreadSfe
struct Bitmap {
public:
Bitmap(const std::string& filePath) {
filePath_ = filePath;
printf("foo %x ctor %sn", this, filePath_.c_str());
}
~Bitmap() {
printf("foo %x dtor %sn", this, filePath_.c_str());
}
std::string filePath_;
};
//@ThreadSafe
struct BitmapCache {
public:
static std::shared_ptr<Bitmap> loadBitmap(const std::string& filePath) {
mutex_.lock();
//whether in the cache
auto iter = cache_.find(filePath);
if (iter != cache_.end()) {
if ((*iter).second) {
return (*iter).second;
} else {
std::shared_ptr<Bitmap> newPtr(new Bitmap(filePath));
(*iter).second = newPtr;
return newPtr;
}
}
//try remove unused elements if possible
if (cache_.size() >= kSlotThreshold) {
std::unordered_map<std::string,std::shared_ptr<Bitmap>>::iterator delIter = cache_.end();
for (auto iter = cache_.begin(); iter != cache_.end(); ++iter) {
auto& item = *iter;
if (item.second && item.second.use_count() == 1) {
delIter = iter;
break;
}
}
if (cache_.end() != delIter) {
(*delIter).second.reset();
cache_.erase(delIter);
}
}
//create new and insert to the cache
std::shared_ptr<Bitmap> newPtr(new Bitmap(filePath));
cache_.insert({filePath, newPtr});
mutex_.unlock();
return newPtr;
}
private:
static const int kSlotThreshold = 20;
static std::mutex mutex_;
static std::unordered_map<std::string,std::shared_ptr<Bitmap>> cache_;
};
/* static */
std::unordered_map<std::string,std::shared_ptr<Bitmap>> BitmapCache::cache_;
/* static */
std::mutex BitmapCache::mutex_;
int main()
{
//test for remove useless element
char buff[200] = {0};
std::vector<std::shared_ptr<Bitmap>> bmpVec(20);
for (int i = 0; i < 20; ++i) {
sprintf_s(buff, 200, "c:\haha%d.bmp", i);
bmpVec[i] = BitmapCache::loadBitmap(buff);
}
bmpVec[3].reset();
std::shared_ptr<Bitmap> newBmp = BitmapCache::loadBitmap("c:\new.bmp");
//test for multiple threading...(to be implemenetd)
return 0;
}
我是c++内存管理的新手。你能给我一个提示吗:我的方法是正确的吗?或者我应该采用不同的设计策略或不同的内存管理器策略(如weak_ptr等)?
这让我想起了Herb Sutter在2013年GoingNative大会上的"最喜欢的c++ 10-Liner"演讲,稍作改编:
std::shared_ptr<Bitmap> get_bitmap(const std::string & path){
static std::map<std::string, std::weak_ptr<Bitmap>> cache;
static std::mutex m;
std::lock_guard<std::mutex> hold(m);
auto sp = cache[path].lock();
if(!sp) cache[path] = sp = std::make_shared<Bitmap>(path);
return sp;
}
的评论:
- 总是使用
std::lock_guard
,而不是调用lock()
和unlock()
。后者更容易出错,也不例外安全。注意,在您的代码中,前两个返回语句从未解锁互斥锁。 这里的想法是跟踪分配的位图对象与 - 如果
path
之前从未出现过,或者对应的Bitmap
已经被删除,cache[path].lock()
将返回空的shared_ptr
;下面的if
语句然后用make_shared
加载Bitmap
,将结果shared_ptr
移赋给sp
,并设置cache[path]
来跟踪新创建的位图。 - 如果
path
对应的Bitmap
仍然活着,那么cache[path].lock()
将创建一个新的shared_ptr
引用它,然后返回。
weak_ptr
在缓存中,所以缓存永远不会保持一个位图本身是活的。这消除了对不再使用的对象进行手动清理的需要——当最后一个引用它们的shared_ptr
被销毁时,它们将被自动删除。相关文章:
- 如果没有malloc,链表实现将失败
- 如何在c++中实现处理器调度模拟器
- 如何在c++中使用引用实现类似python的行为
- 实现无开销push_back的最佳方法是什么
- 使用简单类型列表实现的指数编译时间.为什么
- 如何在BST的这个简单递归实现中消除警告
- 实现一个在集合上迭代的模板函数
- 如何实现缓存友好的动态二进制树
- C++实现文件缓存(如 Web 浏览器)的库
- C++11中的无锁缓存实现
- LRU 缓存C++实现问题
- C++成员函数结果缓存实现
- LRU缓存C++实现
- 在c++中实现LRU缓存-编译错误
- 如何实现线程安全的LRU缓存回收
- 如何使用std::shared_ptr实现缓存管理器
- 如何在c++中实现与抽象类指针向量的缓存一致性
- 如何在树访问器中实现每个节点的缓存
- 实现一个倾斜关联数据缓存c++
- 实现缓存数据的c++ CGI