日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
對象池的使用場景以及自動回收技術(shù)

[[420171]]

 對象池

在編程中,我們經(jīng)常會涉及到對象的操作,而經(jīng)常的操作模式如下圖所示:創(chuàng)建對象->使用對象->銷毀對象。

我們提供的服務(wù)有:成都網(wǎng)站建設(shè)、成都做網(wǎng)站、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、城步ssl等。為上1000家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的城步網(wǎng)站制作公司

而這個對象有可能創(chuàng)建的時(shí)候會需要構(gòu)建很多資源,消耗比較大, 比如:在hiredis的SDK中每次都創(chuàng)建一個redisContext,如果需要查詢,那就首先要進(jìn)行網(wǎng)絡(luò)連接。如果一直都是上圖的工作方式,那將會頻繁的創(chuàng)建連接,查詢完畢后再釋放連接。重新建立連接,讓網(wǎng)絡(luò)的查詢效率降低。

這個時(shí)候就可以構(gòu)建一個對象池來重復(fù)利用這個對象,并且一般要做到線程安全:

  1. 從對象池中獲取對象,如果沒有對象,則創(chuàng)建一個,并返回
  2. 使用對象
  3. 使用完成對象后,將對象還回對象池

那么符合如下條件的,應(yīng)該適合使用對象池技術(shù):

  • 有一些對象雖然創(chuàng)建開銷比較大,但是不一定能夠重復(fù)使用。要使用對象池一定要確保對象能夠重復(fù)使用。
  • 這個對象構(gòu)建的時(shí)候,有一些耗時(shí)的資源可以重復(fù)利用。比如redisContext的網(wǎng)絡(luò)連接。又或者如果對象的頻繁申請釋放會帶來一些其他的資源使用問題,比如內(nèi)存碎片。重復(fù)利用能夠提升程序的效率。
  • 對象池的數(shù)量應(yīng)該控制在能夠接受的范圍內(nèi),并不會無限膨脹。

對象池的實(shí)現(xiàn)

首先介紹一下程序的樣例對象Object, 其就接受一個初始化參數(shù)strInit。

 
 
 
 
  1. class Object 
  2. public: 
  3.   Object(std::string strInit) : m_strInit(strInit)  
  4.   {  
  5.     std::cout << "Object()" << std::endl;  
  6.   } 
  7.   virtual ~Object()  
  8.   {  
  9.     std::cout << "~Object()" << std::endl; 
  10.   } 
  11. private: 
  12.   std::string m_strInit; 
  13. }; 

先來看看對象池的類圖:

  • ObjectPool中采用std::list作為對象池的數(shù)據(jù)結(jié)構(gòu),存儲的對象采用shared_ptr包裹。
  • GetObject獲取一個對象,傳入的參數(shù)為Object需要初始化的信息,如果池子里面沒有,就創(chuàng)建一個返回,如果有就從池子中取出一個返回。
  • ReturnObject 當(dāng)應(yīng)用程序使用完畢后,調(diào)用這個方法還回對象到對象池

然后再來看看代碼吧:

 
 
 
 
  1. class ObjectPool 
  2. public: 
  3.   ObjectPool() { ; } 
  4.   ~ObjectPool() { ; } 
  5.   std::shared_ptr GetObject(std::string strInit) 
  6.   { 
  7.     std::shared_ptr pObject; 
  8.     { 
  9.       std::lock_guard guard(m_mutex); 
  10.       if (!m_lObjects.empty()) 
  11.       { 
  12.         pObject = m_lObjects.front(); 
  13.         m_lObjects.pop_front(); 
  14.       } 
  15.     } 
  16.  
  17.     if (!pObject) 
  18.     { 
  19.       pObject = std::make_shared(strInit); 
  20.     } 
  21.     return pObject; 
  22.   } 
  23.  
  24.   void ReturnObject(std::shared_ptr pObject) 
  25.     if (!pObject) 
  26.       return; 
  27.  
  28.     std::lock_guard guard(m_mutex); 
  29.     m_lObjects.push_front(pObject); 
  30.   } 
  31.  
  32. private: 
  33.   std::mutex m_mutex; 
  34.   std::list> m_lObjects; 
  35. }; 
  36. 那么使用起來比較簡單,如下所示。

     
     
     
     
    1. ObjectPool objPool; 
    2.   auto pObj1 = objPool.GetObject("abc"); 
    3.   //操作對象完成任務(wù) 
    4.   //...... 
    5.   objPool.ReturnObject(pObj1); 

    但是要注意一點(diǎn),有時(shí)候可能使用完了,卻忘記調(diào)用ReturnObject了,這個時(shí)候是否想起了RAII技術(shù)《C++ RAII實(shí)現(xiàn)golang的defer》和《從lock_guard來說一說C++常用的RAII》。

    那么問一問,可以實(shí)現(xiàn)一個自動回收的對象池嗎?不需要調(diào)用者在對象使用完成后,手動將對象歸還給對象池,并且你可能要問:

    1. 針對不同類型的Object,是不是可以用模板去實(shí)現(xiàn)更加通用的實(shí)現(xiàn)一個對象池
    2. 構(gòu)造函數(shù)的參數(shù)列表,也可以是任意的形式

    自動回收的對象池

    要實(shí)現(xiàn)自動回收的對象池,首先要了解unique_ptr和shared_ptr都可以自定義刪除器,也就是說,比如當(dāng)從對象池獲取到的對象是用智能指針包裹的,一般默認(rèn)的刪除器為delete,那我們可以自義定刪除器為: 將這個對象重新放回到對象池. 代碼如下:

     
     
     
     
    1. template 
    2. class ObjectPool 
    3. public: 
    4.   ObjectPool() 
    5.   { 
    6.     m_fObjDeleter = [&](T* pObj) { 
    7.       if (m_bDeconstruct) 
    8.         delete pObj; 
    9.       else 
    10.       { 
    11.         std::lock_guard guard(m_mutex); 
    12.         m_lObjects.push_front(std::shared_ptr(pObj, m_fObjDeleter)); 
    13.       } 
    14.     }; 
    15.   } 
    16.  
    17.   ~ObjectPool() 
    18.   { 
    19.     m_bDeconstruct = true; 
    20.   } 
    21.  
    22.   template 
    23.   std::shared_ptr GetObject(Args&&... args) 
    24.   { 
    25.     std::shared_ptr pObject; 
    26.     { 
    27.       std::lock_guard guard(m_mutex); 
    28.       if (!m_lObjects.empty()) 
    29.       { 
    30.         pObject = m_lObjects.front(); 
    31.         m_lObjects.pop_front(); 
    32.       } 
    33.     } 
    34.  
    35.     if (!pObject) 
    36.     { 
    37.       pObject.reset(new T(std::forward(args)...), m_fObjDeleter); 
    38.     } 
    39.     return pObject; 
    40.   } 
    41.  
    42.   void ReturnObject(std::shared_ptr pObject) 
    43.     if (!pObject) 
    44.       return; 
    45.  
    46.     std::lock_guard guard(m_mutex); 
    47.     m_lObjects.push_front(pObject); 
    48.   } 
    49.  
    50. private: 
    51.   std::function m_fObjDeleter; 
    52.   std::mutex m_mutex; 
    53.   std::list> m_lObjects; 
    54.   volatile bool m_bDeconstruct = false; 
    55. }; 

    自動回收

    關(guān)于自動回收,這個涉及到一個問題,是用unique_ptr還是shared_ptr呢,在這篇大牛寫的文章中進(jìn)行了比較詳細(xì)的闡述《thinking in object pool》(鏈接見參考部分), 說明了應(yīng)該使用unique_ptr,也看到不少人在網(wǎng)上轉(zhuǎn)發(fā)。主要如下闡述:

    因?yàn)槲覀冃枰阎悄苤羔樀哪J(rèn)刪除器改為自定義刪除器,用shared_ptr會很不方便,因?yàn)槟銦o法直接將shared_ptr的刪除器修改為自定義刪除器,雖然你可以通過重新創(chuàng)建一個新對象,把原對象拷貝過來的做法來實(shí)現(xiàn),但是這樣做效率比較低。而unique_ptr由于是獨(dú)占語義,提供了一種簡便的方法方法可以實(shí)現(xiàn)修改刪除器,所以用unique_ptr是最適合的。

    這種方式需要每次都創(chuàng)建一個新對象,并且拷貝原來的對象,是一種比較低效的做法。

    但本人自己進(jìn)行了思考,認(rèn)為可以做到使用shared_ptr一樣實(shí)現(xiàn)了高效的自動回收機(jī)制。首先定義了一個m_fObjDeleter自定義deleter, 不過這種做法可能比較難理解一些,就是定義的m_fObjDeleter函數(shù)內(nèi)也會調(diào)用m_fObjDeleter。當(dāng)shared_ptr引用計(jì)數(shù)為0的時(shí)候,會做如下事情:

    • 如果發(fā)現(xiàn)是OjbectPool調(diào)用了析構(gòu)函數(shù),則直接釋放對象
    • 如果發(fā)現(xiàn)OjbectPool并沒有調(diào)用析構(gòu)函數(shù),則將對象放入對象池中
     
     
     
     
    1. m_fObjDeleter = [&](T* pObj) { 
    2.   if (m_bDeconstruct) 
    3.     delete pObj; 
    4.   else 
    5.   { 
    6.     std::lock_guard guard(m_mutex); 
    7.     m_lObjects.push_front(std::shared_ptr(pObj, m_fObjDeleter)); 
    8.   } 
    9. }; 

    當(dāng)創(chuàng)建對象的時(shí)候指定自定義的deleter:

     
     
     
     
    1. pObject.reset(new T(std::forward(args)...), m_fObjDeleter); 

    模板支持

    使用了模板可以支持通用的對象:

     
     
     
     
    1. template 
    2. class ObjectPool 
    3. public: 
    4.     //...... 
    5.     template 
    6.     std::shared_ptr GetObject(Args&&... args) 
    7.     { 
    8.         //...... 
    9.     } 
    10.  
    11.     void ReturnObject(std::shared_ptr pObject) 
    12.     { 
    13.         //...... 
    14.     } 
    15.  
    16. private: 
    17.     std::function m_fObjDeleter; 
    18.     //..... 
    19.     std::list> m_lObjects; 
    20.     //....... 
    21. }; 

    可變函數(shù)參數(shù)完美轉(zhuǎn)發(fā)

    不同的對象,可能使用的構(gòu)造函數(shù)參數(shù)也不同,那么當(dāng)調(diào)用GetObject的時(shí)候的參數(shù)要設(shè)置為可變參數(shù),其實(shí)現(xiàn)如下:

     
     
     
     
    1. template 
    2. std::shared_ptr GetObject(Args&&... args) 
    3.   std::shared_ptr pObject; 
    4.   { 
    5.     std::lock_guard guard(m_mutex); 
    6.     if (!m_lObjects.empty()) 
    7.     { 
    8.       pObject = m_lObjects.front(); 
    9.       m_lObjects.pop_front(); 
    10.     } 
    11.   } 
    12.  
    13.   if (!pObject) 
    14.   { 
    15.     pObject.reset(new T(std::forward(args)...), m_fObjDeleter); 
    16.   } 
    17.   return pObject; 

    其他

    以上對對象池的基本內(nèi)容進(jìn)行了闡述,那么對于對象池的實(shí)現(xiàn)要根據(jù)場景還有若干的細(xì)節(jié),有些還比較重要:

    • 是否要在啟動的時(shí)候初始化指定數(shù)量的對象?
    • 對象池的數(shù)量是否要設(shè)置一個上限或者下線
    • 對象池重復(fù)利用,當(dāng)取出來后要注意,是不是要對對象做一次reset之類的操作,防止對象上一次的調(diào)用殘留數(shù)據(jù)對本地調(diào)用構(gòu)成影響,這個要根據(jù)自己對象的特點(diǎn)去進(jìn)行相應(yīng)的reset操作
    • 有時(shí)候當(dāng)這個對象可能出現(xiàn)了特別的情況需要銷毀,是否也需要考慮到?
    • 等等

    參考

    • <>模板部分
    • << thinking in object pool >>: https://www.cnblogs.com/qicosmos/p/4995248.html

    本文名稱:對象池的使用場景以及自動回收技術(shù)
    URL網(wǎng)址:http://m.5511xx.com/article/dpccisp.html