当C++遇到IOS应用开发---LRUCache缓存

      本文着重介绍如何在XCODE中,通过C++开发在IOS环境下运行的缓存功能。算法基于LRU(最近最少使用)。有关lru详见:       http://en.wikipedia.org/wiki/Page_replacement_algorithm#Least_recently_used      
      之前在网上看到过网友的一个C++实现,感觉不错,所以核心代码就采用了他的设计。
       原作者通过两个MAP对象来记录缓存数据和LRU队列,注意其中的LRU队列并不是按照常用的方式使用LIST链表,而是使用MAP来代替LIST,有关这一点原作者已做了说明。    
      另外还有人将MRU与LRU组合在一起使用,当然如果清楚了设计原理,那么就很容易理解了,比如这个开源项目:http://code.google.com/p/lru-cache-cpp/
      考虑到缓存实现多数使用单例模式,这里使用C++的模版方式设计了一个Singlton基类,这样以后只要继承该类,子类就会支持单例模式了。其代码如下:

  1. //  
  2. //  SingltonT.h  
  3. //  
  4.  
  5. #ifndef SingltonT_h  
  6. #define SingltonT_h  
  7. #include <iostream>  
  8. #include <tr1/memory>  
  9. usingnamespace std; 
  10. usingnamespace std::tr1; 
  11. template <typename T> 
  12. class Singlton { 
  13. public
  14.       static T* instance(); 
  15.       ~Singlton() { 
  16.           cout << "destruct singlton" << endl; 
  17.       } 
  18. protected
  19.       Singlton(); 
  20. //private:  
  21. protected
  22.       static std::tr1::shared_ptr<T> s_instance; 
  23.       //Singlton();  
  24. }; 
  25.  
  26. template <typename T> 
  27. std::tr1::shared_ptr<T> Singlton<T>::s_instance; 
  28.   
  29. template <typename T> 
  30. Singlton<T>::Singlton() { 
  31.     cout << "construct singlton" << endl; 
  32.   
  33. template <typename T> 
  34. T* Singlton<T>::instance() { 
  35.     if (!s_instance.get()) 
  36.         s_instance.reset(new T); 
  37.     return s_instance.get(); 
//
//  SingltonT.h
//

#ifndef SingltonT_h
#define SingltonT_h
#include <iostream>
#include <tr1/memory>
using namespace std;
using namespace std::tr1;
template <typename T>
class Singlton {
public:
      static T* instance();
      ~Singlton() {
          cout << "destruct singlton" << endl;
      }
protected:
      Singlton();
//private:
protected:
      static std::tr1::shared_ptr<T> s_instance;
      //Singlton();
};

template <typename T>
std::tr1::shared_ptr<T> Singlton<T>::s_instance;
 
template <typename T>
Singlton<T>::Singlton() {
    cout << "construct singlton" << endl;
}
 
template <typename T>
T* Singlton<T>::instance() {
    if (!s_instance.get())
        s_instance.reset(new T);
    return s_instance.get();
}

       另外考虑到在多线程下对static单例对象进行操作,会出现并发访问同步的问题,所以这里使用了读写互斥锁来进行set(设置数据)的同步。如下:

  1. #ifndef _RWLOCK_H_  
  2. #define _RWLOCK_H_  
  3.  
  4. #define LOCK(q) while (__sync_lock_test_and_set(&(q)->lock,1)) {}  
  5. #define UNLOCK(q) __sync_lock_release(&(q)->lock);  
  6.  
  7. struct rwlock { 
  8.     int write; 
  9.     int read; 
  10. }; 
  11.  
  12. staticinlinevoid 
  13. rwlock_init(struct rwlock *lock) { 
  14.     lock->write = 0; 
  15.     lock->read = 0; 
  16.  
  17. staticinlinevoid 
  18. rwlock_rlock(struct rwlock *lock) { 
  19.     for (;;) {//不断循环,直到对读计数器累加成功  
  20.         while(lock->write) { 
  21.             __sync_synchronize(); 
  22.         } 
  23.         __sync_add_and_fetch(&lock->read,1); 
  24.         if (lock->write) {//当已是写锁时,则去掉读锁记数器  
  25.             __sync_sub_and_fetch(&lock->read,1); 
  26.         } else
  27.             break
  28.         } 
  29.     } 
  30.  
  31. staticinlinevoid 
  32. rwlock_wlock(struct rwlock *lock) { 
  33.     __sync_lock_test_and_set(&lock->write,1); 
  34.     while(lock->read) { 
  35.         //http://blog.itmem.com/?m=201204  
  36.         //http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/Atomic-Builtins.html  
  37.         __sync_synchronize();//很重要,如果去掉,g++ -O3 优化编译后的生成的程序会产生死锁  
  38.     } 
  39.  
  40. staticinlinevoid 
  41. rwlock_wunlock(struct rwlock *lock) { 
  42.     __sync_lock_release(&lock->write); 
  43.  
  44. staticinlinevoid 
  45. rwlock_runlock(struct rwlock *lock) { 
  46.     __sync_sub_and_fetch(&lock->read,1); 
#ifndef _RWLOCK_H_
#define _RWLOCK_H_

#define LOCK(q) while (__sync_lock_test_and_set(&(q)->lock,1)) {}
#define UNLOCK(q) __sync_lock_release(&(q)->lock);

struct rwlock {
    int write;
    int read;
};

static inline void
rwlock_init(struct rwlock *lock) {
    lock->write = 0;
    lock->read = 0;
}

static inline void
rwlock_rlock(struct rwlock *lock) {
    for (;;) {//不断循环,直到对读计数器累加成功
        while(lock->write) {
            __sync_synchronize();
        }
        __sync_add_and_fetch(&lock->read,1);
        if (lock->write) {//当已是写锁时,则去掉读锁记数器
            __sync_sub_and_fetch(&lock->read,1);
        } else {
            break;
        }
    }
}

static inline void
rwlock_wlock(struct rwlock *lock) {
    __sync_lock_test_and_set(&lock->write,1);
    while(lock->read) {
        //http://blog.itmem.com/?m=201204
        //http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/Atomic-Builtins.html
        __sync_synchronize();//很重要,如果去掉,g++ -O3 优化编译后的生成的程序会产生死锁
    }
}

static inline void
rwlock_wunlock(struct rwlock *lock) {
    __sync_lock_release(&lock->write);
}

static inline void
rwlock_runlock(struct rwlock *lock) {
    __sync_sub_and_fetch(&lock->read,1);
}

    这里并未使用pthread_mutex_t来设计锁,而是使用了__sync_fetch_and_add指令体系,而相关内容可以参见这个链接:     http://soft.chinabyte.com/os/412/12200912.shtml
    当然最终是否如上面链接中作者所说的比pthread_mutex_t性能要高7-8倍,我没测试过,感兴趣的朋友也可以帮助测试一下。
    有了这两个类之后,我又补充了原文作者中所提到了KEY比较方法的定义,同时引入了id来支持object-c的对象缓存,最终代码修改如下:

  1. #ifndef _MAP_LRU_CACHE_H_  
  2. #define _MAP_LRU_CACHE_H_  
  3.  
  4. #include <string.h>  
  5. #include <iostream>  
  6. #include "rwlock.h"  
  7. #include <stdio.h>  
  8. #include <sys/malloc.h>  
  9. usingnamespace std; 
  10.  
  11. namespace lru_cache { 
  12.     
  13. staticconstint DEF_CAPACITY = 100000;//默认缓存记录数  
  14.  
  15. typedef unsigned longlong virtual_time; 
  16.  
  17. typedefstruct _HashKey 
  18.     NSString* key; 
  19. }HashKey; 
  20.     
  21. typedefstruct _HashValue 
  22.     id value_; 
  23.     virtual_time access_; 
  24. }HashValue; 
  25.  
  26. //仅针对HashKey比较器  
  27. template <class key_t> 
  28. struct hashkey_compare{ 
  29.     bool operator()(key_t x, key_t y) const
  30.         return x < y; 
  31.     } 
  32. }; 
  33.         
  34. template <> 
  35. struct hashkey_compare<HashKey> 
  36.     bool operator()(HashKey __x, HashKey __y) const
  37.         string x = [__x.key UTF8String]; 
  38.         string y = [__y.key UTF8String]; 
  39.         return x < y; 
  40.     } 
  41. }; 
  42.         
  43. //自定义map类型  
  44. template <typename K, typename V, typename _Compare = hashkey_compare<K>, 
  45. typename _Alloc = std::allocator<std::pair<const K, V> > > 
  46. class  lru_map: public map<K, V, _Compare, _Alloc>{}; 
  47.             
  48. class CLRUCache 
  49. public
  50.     
  51.     CLRUCache() : _now(0){ 
  52.         _lru_list = shared_ptr<lru_map<virtual_time, HashKey> >(new lru_map<virtual_time, HashKey>); 
  53.         _hash_table = shared_ptr<lru_map<HashKey, HashValue> > (new lru_map<HashKey, HashValue>); 
  54.     } 
  55.     
  56.     ~CLRUCache(){ 
  57.         _lru_list->clear(); 
  58.         _hash_table->clear(); 
  59.     } 
  60.     
  61.     int set( const HashKey& key, const id &value ) 
  62.     { 
  63.         HashValue hash_value; 
  64.         hash_value.value_ = value; 
  65.         hash_value.access_ = get_virtual_time(); 
  66.         pair< map<HashKey, HashValue>::iterator, bool > ret = _hash_table->insert(make_pair(key, hash_value)); 
  67.         if ( !ret.second ){ 
  68.             // key already exist  
  69.             virtual_time old_access = (*_hash_table)[key].access_; 
  70.             map<virtual_time, HashKey>::iterator iter = _lru_list->find(old_access); 
  71.             if(iter != _lru_list->end()) 
  72.             { 
  73.                 _lru_list->erase(iter); 
  74.             } 
  75.             _lru_list->insert(make_pair(hash_value.access_, key)); 
  76.             (*_hash_table)[key] = hash_value; 
  77.         }        
  78.         else
  79.             _lru_list->insert(make_pair(hash_value.access_, key)); 
  80.             
  81.             if ( _hash_table->size() > DEF_CAPACITY ) 
  82.             { 
  83.                 // get the least recently used key  
  84.                 map<virtual_time, HashKey>::iterator iter = _lru_list->begin(); 
  85.                 _hash_table->erase( iter->second ); 
  86.                 // remove last key from list  
  87.                 _lru_list->erase(iter); 
  88.             } 
  89.         } 
  90.         return 0; 
  91.     } 
  92.     
  93.     HashValue* get( const HashKey& key ) 
  94.     { 
  95.         map<HashKey, HashValue>::iterator iter = _hash_table->find(key); 
  96.         if ( iter != _hash_table->end() ) 
  97.         { 
  98.             virtual_time old_access = iter->second.access_; 
  99.             iter->second.access_ = get_virtual_time(); 
  100.             //调整当前key在LRU列表中的位置  
  101.             map<virtual_time, HashKey>::iterator it = _lru_list->find(old_access); 
  102.             if(it != _lru_list->end()) { 
  103.                 _lru_list->erase(it); 
  104.             } 
  105.             _lru_list->insert(make_pair(iter->second.access_, key)); 
  106.             return &(iter->second); 
  107.         } 
  108.         else
  109.             return NULL; 
  110.         } 
  111.     } 
  112.     
  113.     
  114.     unsigned get_lru_list_size(){ return (unsigned)_lru_list->size(); } 
  115.     unsigned get_hash_table_size() { return (unsigned)_hash_table->size(); } 
  116.     virtual_time get_now() { return _now; } 
  117.     
  118. private
  119.     virtual_time get_virtual_time() 
  120.     { 
  121.         return ++_now; 
  122.     } 
  123.     
  124.     shared_ptr<lru_map<virtual_time, HashKey> >    _lru_list; 
  125.     shared_ptr<lru_map<HashKey, HashValue> > _hash_table; 
  126.     virtual_time _now; 
  127. }; 
  128.     
  129. #endif 
#ifndef _MAP_LRU_CACHE_H_
#define _MAP_LRU_CACHE_H_

#include <string.h>
#include <iostream>
#include "rwlock.h"
#include <stdio.h>
#include <sys/malloc.h>
using namespace std;

namespace lru_cache {
   
static const int DEF_CAPACITY = 100000;//默认缓存记录数

typedef unsigned long long virtual_time;

typedef struct _HashKey
{
    NSString* key;
}HashKey;
   
typedef struct _HashValue
{
    id value_;
    virtual_time access_;
}HashValue;

//仅针对HashKey比较器
template <class key_t>
struct hashkey_compare{
    bool operator()(key_t x, key_t y) const{
        return x < y;
    }
};
       
template <>
struct hashkey_compare<HashKey>
{
    bool operator()(HashKey __x, HashKey __y) const{
        string x = [__x.key UTF8String];
        string y = [__y.key UTF8String];
        return x < y;
    }
};
       
//自定义map类型
template <typename K, typename V, typename _Compare = hashkey_compare<K>,
typename _Alloc = std::allocator<std::pair<const K, V> > >
class  lru_map: public map<K, V, _Compare, _Alloc>{};
           
class CLRUCache
{
public:
   
    CLRUCache() : _now(0){
        _lru_list = shared_ptr<lru_map<virtual_time, HashKey> >(new lru_map<virtual_time, HashKey>);
        _hash_table = shared_ptr<lru_map<HashKey, HashValue> > (new lru_map<HashKey, HashValue>);
    }
   
    ~CLRUCache(){
        _lru_list->clear();
        _hash_table->clear();
    }
   
    int set( const HashKey& key, const id &value )
    {
        HashValue hash_value;
        hash_value.value_ = value;
        hash_value.access_ = get_virtual_time();
        pair< map<HashKey, HashValue>::iterator, bool > ret = _hash_table->insert(make_pair(key, hash_value));
        if ( !ret.second ){
            // key already exist
            virtual_time old_access = (*_hash_table)[key].access_;
            map<virtual_time, HashKey>::iterator iter = _lru_list->find(old_access);
            if(iter != _lru_list->end())
            {
                _lru_list->erase(iter);
            }
            _lru_list->insert(make_pair(hash_value.access_, key));
            (*_hash_table)[key] = hash_value;
        }       
        else {
            _lru_list->insert(make_pair(hash_value.access_, key));
           
            if ( _hash_table->size() > DEF_CAPACITY )
            {
                // get the least recently used key
                map<virtual_time, HashKey>::iterator iter = _lru_list->begin();
                _hash_table->erase( iter->second );
                // remove last key from list
                _lru_list->erase(iter);
            }
        }
        return 0;
    }
   
    HashValue* get( const HashKey& key )
    {
        map<HashKey, HashValue>::iterator iter = _hash_table->find(key);
        if ( iter != _hash_table->end() )
        {
            virtual_time old_access = iter->second.access_;
            iter->second.access_ = get_virtual_time();
            //调整当前key在LRU列表中的位置
            map<virtual_time, HashKey>::iterator it = _lru_list->find(old_access);
            if(it != _lru_list->end()) {
                _lru_list->erase(it);
            }
            _lru_list->insert(make_pair(iter->second.access_, key));
            return &(iter->second);
        }
        else{
            return NULL;
        }
    }
   
   
    unsigned get_lru_list_size(){ return (unsigned)_lru_list->size(); }
    unsigned get_hash_table_size() { return (unsigned)_hash_table->size(); }
    virtual_time get_now() { return _now; }
   
private:
    virtual_time get_virtual_time()
    {
        return ++_now;
    }
   
    shared_ptr<lru_map<virtual_time, HashKey> >    _lru_list;
    shared_ptr<lru_map<HashKey, HashValue> > _hash_table;
    virtual_time _now;
};
   
#endif

      接下来看一下如果结合单例和rwlock来设计最终的缓存功能,如下:

  1. usingnamespace lru_cache; 
  2. class DZCache: public Singlton<DZCache> 
  3.     friend  class Singlton<DZCache>; 
  4. private
  5.     shared_ptr<CLRUCache> clu_cache; 
  6.     rwlock *lock; 
  7.     DZCache(){ 
  8.         lock =(rwlock*) malloc(sizeof(rwlock)); 
  9.         rwlock_init(lock); 
  10.         clu_cache = shared_ptr<CLRUCache>(new CLRUCache()); 
  11.         cout << "construct JobList" << endl; 
  12.     } 
  13.     
  14.     DZCache * Instance() { 
  15.         return s_instance.get(); 
  16.     } 
  17.  
  18. public
  19.     
  20.     ~DZCache(){ 
  21.         free(lock); 
  22.     } 
  23.     
  24.     static DZCache& getInstance(){ 
  25.         return *instance(); 
  26.     } 
  27.  
  28.     void set(NSString* key, id value){ 
  29.         //加锁  
  30.         rwlock_wlock(lock); 
  31.         HashKey hash_key; 
  32.         hash_key.key = key; 
  33.         clu_cache->set(hash_key, value); 
  34.         rwlock_wunlock(lock); 
  35.     } 
  36.     
  37.     id get(NSString* key){ 
  38.         HashKey hash_key; 
  39.         hash_key.key = key; 
  40.         HashValue* value = clu_cache->get(hash_key); 
  41.         if(value == NULL){ 
  42.             return nil; 
  43.         } 
  44.         else
  45.             return value->value_; 
  46.         } 
  47.     } 
  48. }; 
  49.  
  50. #endif 
using namespace lru_cache;
class DZCache: public Singlton<DZCache>
{
    friend  class Singlton<DZCache>;
private:
    shared_ptr<CLRUCache> clu_cache;
    rwlock *lock;
    DZCache(){
        lock =(rwlock*) malloc(sizeof(rwlock));
        rwlock_init(lock);
        clu_cache = shared_ptr<CLRUCache>(new CLRUCache());
        cout << "construct JobList" << endl;
    }
   
    DZCache * Instance() {
        return s_instance.get();
    }

public:
   
    ~DZCache(){
        free(lock);
    }
   
    static DZCache& getInstance(){
        return *instance();
    }

    void set(NSString* key, id value){
        //加锁
        rwlock_wlock(lock);
        HashKey hash_key;
        hash_key.key = key;
        clu_cache->set(hash_key, value);
        rwlock_wunlock(lock);
    }
   
    id get(NSString* key){
        HashKey hash_key;
        hash_key.key = key;
        HashValue* value = clu_cache->get(hash_key);
        if(value == NULL){
            return nil;
        }
        else{
            return value->value_;
        }
    }
};

#endif

    最后看一下如何使用:

[cpp] view plaincopyprint?
  1. void testLRUCache(){ 
  2.     //指针方式  
  3.     DZCache::instance()->set(@"name", @"daizhj");//设置  
  4.     NSString* name = (NSString*)DZCache::instance()->get(@"name");//获取  
  5.     std::cout<<[name UTF8String]<<endl; 
  6.     
  7.     NSNumber * age=[NSNumber numberWithInt:123123]; 
  8.     DZCache::instance()->set(@"age", age); 
  9.     age = (NSNumber*)DZCache::instance()->get(@"age"); 
  10.  
  11.     //对象方式  
  12.     DZCache::getInstance().set(@"name", @"daizhenjun"); 
  13.     name = (NSString*)DZCache::getInstance().get(@"name"); 
  14.     std::cout<<[name UTF8String]<<endl; 
  15.     
  16.     age = [NSNumber numberWithInt:123456]; 
  17.     DZCache::getInstance().set(@"age", age); 
  18.     age = (NSNumber*)DZCache::getInstance().get(@"age"); 
void testLRUCache(){
    //指针方式
    DZCache::instance()->set(@"name", @"daizhj");//设置
    NSString* name = (NSString*)DZCache::instance()->get(@"name");//获取
    std::cout<<[name UTF8String]<<endl;
   
    NSNumber * age=[NSNumber numberWithInt:123123];
    DZCache::instance()->set(@"age", age);
    age = (NSNumber*)DZCache::instance()->get(@"age");

    //对象方式
    DZCache::getInstance().set(@"name", @"daizhenjun");
    name = (NSString*)DZCache::getInstance().get(@"name");
    std::cout<<[name UTF8String]<<endl;
   
    age = [NSNumber numberWithInt:123456];
    DZCache::getInstance().set(@"age", age);
    age = (NSNumber*)DZCache::getInstance().get(@"age");
}
posted @ 2012-11-17 21:34  漫步云计算  阅读(182)  评论(0编辑  收藏  举报