九九热这里直有精品,1区二区三区在线播放,玖玖爱在线观看资源,国产aⅴ综合网,午夜福利男女,日本亚洲欧美三级,日韩无码黄色导航,内射少妇13区,中文字幕高清网

您身邊的軟件定制專家--9年開發(fā)經(jīng)驗為您護航

18678812288
0531-88887250

C++實現(xiàn)最基本的LRUCache服務(wù)器緩存

文章作者:濟南軟件開發(fā) 時間:2016年11月08日

  后臺開發(fā)必備知識,不過我不是搞這個的,只是因為很久以前就想寫這些東西,事情多,拖到現(xiàn)在。寫的過程里面發(fā)現(xiàn)很多問題,不會全部說,最后會順帶提一提。

  注意,本篇筆記只是對接口寫法做了記錄,并沒有進行更嚴(yán)格的設(shè)計和限制,包括更嚴(yán)密的封裝,這里只是學(xué)習(xí)它實現(xiàn)的原理。

  不過有些idea還是要知道的,系統(tǒng)定時對緩存進行清除并加入滿足條件的新數(shù)據(jù),是根據(jù):訪問時間,訪問次數(shù),可用緩存容量(分配到的內(nèi)存)等因素決定的,實際設(shè)計其實很多東西需要考慮。

  一、介紹:

  LRU,Least Recently Used,最近最少使用,服務(wù)器緩存常用算法的一種。

  比如說一些系統(tǒng)登錄的操作,不可能每次你訪問系統(tǒng)都去調(diào)用數(shù)據(jù)庫的東西,如果能劃出一些空間來,比如說500M,用來緩存這些東西,這樣用戶訪問的時候先在緩存里找,找不到,再去訪問數(shù)據(jù)庫,同時把被訪問的內(nèi)容放到緩存里面(我們可以假設(shè)這些東西還會經(jīng)常被訪問)。然而,我們分配用來做緩存(Cache)的空間肯定是有限的,總不可能從數(shù)據(jù)庫讀的東西全部放到緩存里,所以,當(dāng)緩存里的內(nèi)容達到上限值的時候,我們就要把最少使用的東西寫回數(shù)據(jù)庫,再將新的訪問內(nèi)容從數(shù)據(jù)庫暫存到緩存里面。

  二、數(shù)據(jù)結(jié)構(gòu):

  最常用的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)方式是hash_map和Double-Linked List,hash_map只是為了實現(xiàn)鍵值key-value對應(yīng),這樣就避免了每次尋找特定值都要在雙線鏈表里面順序查找,服務(wù)器是沒辦法負(fù)擔(dān)這種低效率的查找方法的。

  我們可以為鏈表節(jié)點寫一個結(jié)構(gòu)體,用來定義節(jié)點的類型;然后專門寫一個類用來組織緩存信息的存放——以雙鏈表的形式。

  復(fù)制代碼

  template<class K, class T>

  struct Node

  {

  K key;

  T data;

  Node *next;

  Node *prev;

  };

  復(fù)制代碼

  復(fù)制代碼

  template<class K,class T>

  class LRUCache

  {

  public:

  LRUCache(size_t size);         //typedef unsigned int size_t

  ~LRUCache();

  void Put(K key,T data);

  T Get(K key);

  private:

  void Attach(Node<K,T>* node);

  void Detach(Node<K,T>* node);

  private:

  hash_map<K,Node<K,T>*> hashmap;

  vector<Node<K,T>*> linkedList;

  Node<K,T>* head;

  Node<K,T>* tail;

  Node<K,T>* entries;          //temp nodes

  };

  復(fù)制代碼

  看代碼太枯燥,我畫了個UML圖:

  三、主要的兩個函數(shù)接口Put()和Get():

  最基本的,不是存,就是取。那修改呢?合并到存里面去了,通過鍵值key查找一個hash_map對應(yīng)的value,如果value不是NULL,那么更新value的內(nèi)容即可。其實服務(wù)器緩存比較多作的是讀多寫少的東西。

  因為代碼實在是太枯燥了,所以針對Put函數(shù)和Get函數(shù)畫了兩張流程圖:

  其中,Detach(node)表示將這個節(jié)點從雙鏈表中解出,成為一個獨立的節(jié)點;Attach(node)表示將node插入到頭節(jié)點head后面(表示它可能再次被用到),這樣的話,如果自己再設(shè)計一個GetFirst()函數(shù),就能直接獲取上次的訪問結(jié)果,這種訪問連hash_map都不需要用到。

  流程講解:

  在上述Put函數(shù)流程圖中,注意第一個判斷“node==NULL”,這個node地址是通過hashmap映射而來的:1、如果不是NULL,說明這個節(jié)點已經(jīng)存在,那么將該節(jié)點的數(shù)據(jù)data重寫以后加到鏈表頭;

  2、如果是NULL,還要進行第二個判斷“分配地址的linkedList是不是已經(jīng)空了”:

  2.1、如果空了,說明全部可用地址已經(jīng)分配完了,那么,將原鏈表的最后一個節(jié)點踢出鏈表(應(yīng)寫入數(shù)據(jù)庫),然后將被踢出點的hashmap中對應(yīng)的key-value擦除,然后再加入新節(jié)點,并在hashmap中對應(yīng)好新節(jié)點的key-value;

  2.2、如果不空,那么從linkedList中分配個新地址給這個節(jié)點,同時linkedList要彈出分配完的地址,然后再將新節(jié)點加入鏈表中,對應(yīng)好hashmap中的key-value。

  要注意,hashmap用來對應(yīng)key-value,這里是方便查找;而vector變量linkedList也只是在初始化的時候存儲了一塊連續(xù)地址,用來分配地址,它們兩者都不是用來直接構(gòu)建鏈表的,鏈表是你自己建立的。

  Get()函數(shù)就比較簡單了:

  四、C++代碼實現(xiàn):

  代碼不是我原創(chuàng)的(后面給出原文地址),不過理清思路之后我自己實現(xiàn)了一遍,測試的過程實在是各種奇葩和辛苦(因為一個不注意的小地方)。

  代碼實現(xiàn)里用到了hash_map,注意,這個不是C++標(biāo)準(zhǔn)的一部分,所以你要自己去庫文件里找找,一般來說庫文件都是在/usr/include下面的了,cd到這個文件夾,然后用grep找一下:

  cd /usr/include

  grep  -R "hash_map"

  最后你會發(fā)現(xiàn)是這個頭文件:<ext/hash_map>,用文本文檔打開來看一下,因為是限制了命名空間的,會發(fā)現(xiàn)有兩個可用的命名空間,其中一個是__gnu_cxx。

  代碼我一開始是用Qt寫的,不過后來發(fā)現(xiàn)Qt沒法調(diào)試(后面再說),于是最后使用Eclipse完成了調(diào)試和測試,下面先給出代碼:

  LRUCache

  調(diào)試的時候我發(fā)現(xiàn)了一個問題:

  坑爹了,全部節(jié)點的next指針全部都指向自己,這樣的話鏈表長得像什么樣子呢?應(yīng)該是這樣:

  這個錯誤到底是怎么來的?

  我反復(fù)地看代碼,節(jié)點的鏈入(Attach)和取出(Detach)都是沒有問題的,而且,插入新節(jié)點的時候,已經(jīng)插入過的節(jié)點為什么沒有了?Attach方法既然是正確的,那為什么節(jié)點的next為什么會指向自己?

  綜合上面兩個問題,我突然意識到:那只能是分配地址的時候出現(xiàn)問題了!

  所以回到構(gòu)造函數(shù)分配地址的部分,我發(fā)現(xiàn)在for循環(huán)里面,本應(yīng)是:

  linkedList.push_back(entries+i);

  這樣就能順序存儲分配好的地址。

  但我竟然把i寫成了1,所以每個地址都成了同一個!吐血的經(jīng)歷。


想要了解更多詳情歡迎來電咨詢18678812288
登陸網(wǎng)址:m.h6244.cn。
聯(lián)系人:王經(jīng)理。

祁阳县| 海淀区| 凤台县| 温泉县| 昌江| 车致| 青龙| 湘潭市| 铁力市| 阿拉善盟| 永年县| 通州区| 西峡县| 青河县| 铁岭市| 奉化市| 繁峙县| 灵川县| 镇沅| 任丘市| 伊宁市| 集贤县| 上蔡县| 屯昌县| 察雅县| 池州市| 清河县| 临澧县| 昌都县| 新丰县| 广州市| 昌邑市| 安丘市| 平原县| 宣恩县| 金坛市| 吉首市| 金秀| 万州区| 凌海市| 三亚市|