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组合在一起使用,当然如果清楚了设计原理,那么就很容易理解了。
考虑到缓存实现多数使用单例模式,这里使用C++的模版方式设计了一个Singlton基类,这样以后只要继承该类,子类就会支持单例模式了。其代码如下:


代码如下:

//
// 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();
void print() {
cout << "haha" << endl;
}
~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(设置数据)的同步。如下:


代码如下:

#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指令体系,当然最终是否如上面链接中作者所说的比pthread_mutex_t性能要高7-8倍,我没测试过,感兴趣的朋友也可以帮助测试一下。
有了这两个类之后,我又补充了原文作者中所提到了KEY比较方法的定义,同时引入了id来支持object-c的对象缓存,最终代码修改如下:


代码如下:

#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来设计最终的缓存功能,如下:


代码如下:

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

最后看一下如何使用:


代码如下:

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");
}

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

(0)

相关推荐

  • iOS开发之清除缓存功能的实现

    前言 移动应用在处理网络资源时,一般都会做离线缓存处理,其中以图片缓存最为典型,其中很流行的离线缓存框架为SDWebImage.但是,离线缓存会占用手机存储空间,所以缓存清理功能基本成为资讯.购物.阅读类app的标配功能. 清除缓存基本上都是在设置界面的某一个Cell,于是我们可以把清除缓存封装在某一个自定义Cell中 如下图所示: 实现的具体步骤 使用注意:过程中需要用到第三方库,请提前安装好:SDWebImage.SVProgressHUD. 1. 创建自定义Cell,命名为GYLClear

  • iOS清除所有缓存的实例代码

    本文介绍了iOS清除所有缓存的实例代码,分享给大家,具体如下: 计算缓存 NSString *libPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0]; CGFloat fileSize=[self folderSizeAtPath:libPath]; - (float ) folderSizeAtPath:(NSString*) folderPath{ NSFileMa

  • iOS中的缓存计算和清除完整实例代码

    1.首先,一般我们项目中的缓存一般分为2大块,一个是自己缓存的一些数据;还有一个就是我们使用的SDWebImage这个第三方库给我们自动缓存的图片文件缓存了 <1>怎么计算缓存大小(主要是利用系统提供的NSFileManager类来实现) $1.单个文件大小的计算 -(long long)fileSizeAtPath:(NSString *)path{ NSFileManager *fileManager=[NSFileManager defaultManager]; if([fileMana

  • iOS系统缓存方面开发的相关基础

    一.关于同一个URL的多次请求   有时候,对同一个URL请求多次,返回的数据可能都是一样的,比如服务器上的某张图片,无论下载多少次,返回的数据都是一样的. 上面的情况会造成以下问题   (1)用户流量的浪费   (2)程序响应速度不够快   解决上面的问题,一般考虑对数据进行缓存.     二.缓存   为了提高程序的响应速度,可以考虑使用缓存(内存缓存\硬盘缓存) 第一次请求数据时,内存缓存中没有数据,硬盘缓存中没有数据.   缓存数据的过程 当服务器返回数据时,需要做以下步骤   (1)使

  • iOS中设置清除缓存功能的实现方法

    绝大多数应用中都存在着清楚缓存的功能,形形色色,各有千秋,现为大家介绍一种最基础的清除缓存的方法.清除缓存基本上都是在设置界面的某一个Cell,于是我们可以把清除缓存封装在某一个自定义Cell中,如下图所示: 具体步骤 使用注意:过程中需要用到第三方库,请提前安装好:SDWebImage.SVProgressHUD. 1. 创建自定义Cell,命名为GYLClearCacheCell 重写initWithStyle:(UITableViewCellStyle)style reuseIdentif

  • IOS中计算缓存文件的大小判断实例详解

    IOS中计算缓存文件的大小判断实例详解 IOS中计算缓存文件的大小判断,在这里分享一下自己的心得,希望和大家一起分享技术,如果有什么不足,还请大家指正.写出这篇目的,就是希望大家一起成长,我也相信技术之间没有高低,只有互补,只有分享,才能使彼此更加成长. 实例代码: //获取缓存文件路径 -(NSString *)getCachesPath{ // 获取Caches目录路径 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCaches

  • Objective-C的缓存框架EGOCache在iOS App开发中的使用

    EGOCache简介 EGOCache is a simple, thread-safe key value cache store. It has native support for NSString, UI/NSImage, and NSData, but can store anything that implements <NSCoding>. All cached items expire after the timeout, which by default, is one da

  • 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,有关

  • Nginx环境下WordPress的多站点功能配置详解

    WordPress的多站点功能允许安装一个WordPress程序的情况下,实现多个站点(也就是一套程序,可以绑定多个域名或子域名). 每个站点拥有独立的主题.插件.文章以及页面. 这样可以极大的减少了维护和更新多个WordPress安装程序的麻烦, 并且,每个站点之间又能够相互独立,互不影响. WordPress multisite有两个方式:子目录和子域名,这里我们主要介绍子域名方式. 也就是说,在主域名的基础上,我们会创建一个子域名,例如:http://shop.jb51.com. 同时,我

  • 在IOS环境下配置DHCP服务6500

    Keywords: DHCP SERVER IOS pool client-identifier excluded-address helper-address spantree portfast 一位客户想把DHCP SERVER迁移到6509交换机的MSFC上,要求还挺复杂: 1.同时为多个VLAN的客户机分配地址 2.VLAN内有部分地址采用手工分配的方式 3.为客户指定网关.Wins服务器等 4.VLAN 2的地址租用有效期限为1天,其它为3天 5.按MAC地址为特定用户分配指定的IP地

  • Apache环境下PHP利用HTTP缓存协议原理解析及应用分析

    对于静态页面还有Etag. 一.先来看第一种情况:apache 静态页面 apache发送给客户端的静态页面一般包含Last-Modified和Etag,这两个标签的值来自静态文件的修改时间和inode. 下面是截取得apache返回客户端的头 XML/HTML代码 复制代码 代码如下: Last-Modified: Fri, 26 Jan 2007 01:53:34 GMT ETag: "3f9f640-318-cb9f8380" 搜索引擎之所以喜欢静态文件是因为有这两个标识,可以判

  • Go 多环境下配置管理方案(多种方案)

    目录 需求 方案1:配置文件管理 方案2:集中式管理配置 需求 开发过程中开发者经常面对的一个需求就是:一个项目可能会在不同的环境下运行,本地开发环境.测试环境.灰度环境.生产环境.每个环境的参数和配置可能都会不相同,如服务器配置.数据库连接.为避免各环境产生数据混乱,让程序执行时在不同的环境中调用正确的配置,可以这样设计: 命令唤醒程序--->识别环境--->根据环境读取对应配置文件 方案1:配置文件管理 根据环境创建配置文件,多个环境多个配置文件.如开发环境 config-dev.yaml

  • webuploader在springMVC+jquery+Java开发环境下的大文件分片上传的实例代码

    注意: 1,webuploader上传组件会和jQuery自带的上传组件冲突,所以不要使用<form>标签中添加上传文件的属性; enctype="multipart/form-data" 2.并且屏蔽ApplicationContext-mvc.xml里面的拦截配置! <!-- 上传拦截,如最大上传值及最小上传值 --> <!--新增加的webuploader上传组件,必须要屏蔽这里的拦截机制 <bean id="multipartRes

  • Windows环境下PHP开发环境搭建 - 图文完全教程

    基于Windows环境下的PHP开发环境搭建 (apache+mysql+php) 一.准备工作 Apache2.2.11 下载地址:http://www.apache.org MySQL5.0 下载地址:http://www.mysql.com PHP5.2.9 下载地址:http://www.php.net 二.配置PHP 1. 将php-5.2.9-win32.zip解压缩到指定位置(如C:\ ),并将其改为php[如下图] 2.打开php文件夹,并将php.ini-dist更名为php.

  • 在Vue环境下利用worker运行interval计时器的步骤

    今天在code review时,发现之前遗留的问题: 在一个视频播放页面,有一个40ms的interval一直在阻碍,导致视频延时逐渐增大 于是写了一个worker单独把计时器拉出去跑了 实现步骤如下 由于用的是vue-cli,在webpack下要安装worker-loader依赖才能单独加载worker.js npm install worker-loader --save-dev 更改 vue.config.js 文件的配置项 configureWebpack:{ module: { rul

  • win10环境下配置vscode python开发环境的教程详解

    前言 VScode是一个相当优秀的IDE,具备开源.跨平台.模块化.插件丰富.启动时间快.颜值高.可高度定制等等优秀的特质,不愧是微软爸爸的私生子. 所以用VScode来编写Python,也是相当的好用的. 1:安装python,我安装的是python3.7(注意python安装的时候要点击上add path的选型,即安装的时候顺便在windows系统上注册了python安装的路径) 2:安装vscode 3:vscode安装比较常用的插件: 4:设置vscode:添加python的路径(可以支

  • win10环境下vscode Linux C++开发代码自动提示配置(基于WSL)

    安装 vs code, 安装 c/c++ 插件 C/C++ IntelliSense,TabNine,Bracket Pair Colorizer. 在win10下打开linux子系统设置(具体搜索教程),在应用商店安装ubutu16. 打开File->Preferences->settings,编辑settings.json. copy 以下的内容替换settings.json "C_Cpp.default.compilerPath": "/usr/bin/g+

随机推荐