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

来源:互联网 发布:淘宝自动打款时间 编辑:程序博客网 时间:2024/05/16 06:10

http://blog.csdn.net/daizhj/article/details/8178807


 本文着重介绍如何在XCODE中,通过C++开发在IOS环境下运行的缓存功能。算法基于LRU(最近最少使用)。有关lru详见:
      http://en.wikipedia.org/wiki/Page_replacement_algorithm#Least_recently_used
      
      之前在网上看到过网友的一个C++实现,感觉不错,所以核心代码就采用了他的设计。相关链接如下:
      http://www.cppblog.com/red22/articles/62499.html

      原作者通过两个MAP对象来记录缓存数据和LRU队列,注意其中的LRU队列并不是按照常用的方式使用LIST链表,而是使用MAP来代替LIST,有关这一点原作者已做了说明。
    
      另外还有人将MRU与LRU组合在一起使用,当然如果清楚了设计原理,那么就很容易理解了,比如这个开源项目:http://code.google.com/p/lru-cache-cpp/

      考虑到缓存实现多数使用单例模式,这里使用C++的模版方式设计了一个Singlton基类,这样以后只要继承该类,子类就会支持单例模式了。其代码如下:
 

[cpp] view plaincopy
  1. //  
  2. //  SingltonT.h  
  3. //  
  4.   
  5. #ifndef SingltonT_h  
  6. #define SingltonT_h  
  7. #include <iostream>  
  8. #include <tr1/memory>  
  9. using namespace std;  
  10. using namespace 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.    
  34. template <typename T>  
  35. T* Singlton<T>::instance() {  
  36.     if (!s_instance.get())  
  37.         s_instance.reset(new T);  
  38.     return s_instance.get();  
  39. }  



       另外考虑到在多线程下对static单例对象进行操作,会出现并发访问同步的问题,所以这里使用了读写互斥锁来进行set(设置数据)的同步。如下:
[cpp] view plaincopy
  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. static inline void  
  13. rwlock_init(struct rwlock *lock) {  
  14.     lock->write = 0;  
  15.     lock->read = 0;  
  16. }  
  17.   
  18. static inline void  
  19. rwlock_rlock(struct rwlock *lock) {  
  20.     for (;;) {//不断循环,直到对读计数器累加成功  
  21.         while(lock->write) {  
  22.             __sync_synchronize();  
  23.         }  
  24.         __sync_add_and_fetch(&lock->read,1);  
  25.         if (lock->write) {//当已是写锁时,则去掉读锁记数器  
  26.             __sync_sub_and_fetch(&lock->read,1);  
  27.         } else {  
  28.             break;  
  29.         }  
  30.     }  
  31. }  
  32.   
  33. static inline void  
  34. rwlock_wlock(struct rwlock *lock) {  
  35.     __sync_lock_test_and_set(&lock->write,1);  
  36.     while(lock->read) {  
  37.         //http://blog.itmem.com/?m=201204  
  38.         //http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/Atomic-Builtins.html  
  39.         __sync_synchronize();//很重要,如果去掉,g++ -O3 优化编译后的生成的程序会产生死锁  
  40.     }  
  41. }  
  42.   
  43. static inline void  
  44. rwlock_wunlock(struct rwlock *lock) {  
  45.     __sync_lock_release(&lock->write);  
  46. }  
  47.   
  48. static inline void  
  49. rwlock_runlock(struct rwlock *lock) {  
  50.     __sync_sub_and_fetch(&lock->read,1);  
  51. }  



    这里并未使用pthread_mutex_t来设计锁,而是使用了__sync_fetch_and_add指令体系,而相关内容可以参见这个链接:
    http://soft.chinabyte.com/os/412/12200912.shtml

    当然最终是否如上面链接中作者所说的比pthread_mutex_t性能要高7-8倍,我没测试过,感兴趣的朋友也可以帮助测试一下。

    有了这两个类之后,我又补充了原文作者中所提到了KEY比较方法的定义,同时引入了id来支持object-c的对象缓存,最终代码修改如下:

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




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

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




    最后看一下如何使用:
[cpp] view plaincopy
  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");  
  19. }  




    好了,今天的内容就先到这里了。

    原文链接:http://www.cnblogs.com/daizhj/archive/2012/11/13/2768092.html
    作者: daizhj, 代震军 
    微博: http://weibo.com/daizhj
    Tags:ios, c++, lru, cache, map

0 0