2014-06-24 81 views
1
#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 %s\n", this, filePath_.c_str()); 
     } 
     ~Bitmap() { 
      printf("foo %x dtor %s\n", 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等)?如何使用std :: shared_ptr實現緩存管理器?

回答

11

這在GoingNative 2013讓我想起了香草薩特的"Favorite 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; 
} 

評論:

  1. 始終使用std::lock_guard而不是調用互斥lock()unlock()。後者更容易出錯並且不是異常安全的。請注意,在您的代碼中,前兩個返回語句從未解鎖互斥鎖。
  2. 這裏的想法是跟蹤分配的位圖對象在緩存中的一個weak_ptr,所以緩存從來沒有本身保持位圖活着。這消除了手動清理那些不再使用的對象的需求 - 當最後shared_ptr引用它們被破壞,它們會自動刪除。
  3. 如果path從未見過,或者如果相應的Bitmap已被刪除,cache[path].lock()將返回一個空的shared_ptr;下面if語句然後加載Bitmapmake_shared,移動,分配所產生的shared_ptrsp,並設置cache[path]來跟蹤新創建位圖。
  4. 如果Bitmap對應的path仍然存在,那麼cache[path].lock()將創建一個新的shared_ptr引用它,然後返回。
+0

你能解釋一下爲什麼使用std :: weak_ptr的?謝謝。 – Jichao

+0

@Jichao見編輯。 –

+3

這個緩存太糟糕了,並沒有做太多的緩存。如果我需要每幀的位圖來繪製背景,則每次都會加載並丟棄它。它比「緩存/管理器」更像是一個「加載器/重複數據刪除器」。 – DanielKO