自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

對(duì)象池的使用場(chǎng)景以及自動(dòng)回收技術(shù)

開(kāi)發(fā) 后端
在編程中,我們經(jīng)常會(huì)涉及到對(duì)象的操作,而經(jīng)常的操作模式如下圖所示:創(chuàng)建對(duì)象->使用對(duì)象->銷毀對(duì)象。

[[420171]]

 對(duì)象池

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

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

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

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

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

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

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

首先介紹一下程序的樣例對(duì)象Object, 其就接受一個(gè)初始化參數(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. }; 

先來(lái)看看對(duì)象池的類圖:

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

然后再來(lái)看看代碼吧:

  1. class ObjectPool 
  2. public
  3.   ObjectPool() { ; } 
  4.   ~ObjectPool() { ; } 
  5.   std::shared_ptr<Object> GetObject(std::string strInit) 
  6.   { 
  7.     std::shared_ptr<Object> pObject; 
  8.     { 
  9.       std::lock_guard<std::mutex> 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<Object>(strInit); 
  20.     } 
  21.     return pObject; 
  22.   } 
  23.  
  24.   void ReturnObject(std::shared_ptr<Object> pObject) 
  25.     if (!pObject) 
  26.       return
  27.  
  28.     std::lock_guard<std::mutex> guard(m_mutex); 
  29.     m_lObjects.push_front(pObject); 
  30.   } 
  31.  
  32. private: 
  33.   std::mutex m_mutex; 
  34.   std::list<std::shared_ptr<Object>> m_lObjects; 
  35. }; 

那么使用起來(lái)比較簡(jiǎn)單,如下所示。

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

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

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

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

自動(dòng)回收的對(duì)象池

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

  1. template<typename T> 
  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<std::mutex> guard(m_mutex); 
  12.         m_lObjects.push_front(std::shared_ptr<T>(pObj, m_fObjDeleter)); 
  13.       } 
  14.     }; 
  15.   } 
  16.  
  17.   ~ObjectPool() 
  18.   { 
  19.     m_bDeconstruct = true
  20.   } 
  21.  
  22.   template<typename... Args> 
  23.   std::shared_ptr<T> GetObject(Args&&... args) 
  24.   { 
  25.     std::shared_ptr<T> pObject; 
  26.     { 
  27.       std::lock_guard<std::mutex> 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>(args)...), m_fObjDeleter); 
  38.     } 
  39.     return pObject; 
  40.   } 
  41.  
  42.   void ReturnObject(std::shared_ptr<T> pObject) 
  43.     if (!pObject) 
  44.       return
  45.  
  46.     std::lock_guard<std::mutex> guard(m_mutex); 
  47.     m_lObjects.push_front(pObject); 
  48.   } 
  49.  
  50. private: 
  51.   std::function<void(T* pObj)> m_fObjDeleter; 
  52.   std::mutex m_mutex; 
  53.   std::list<std::shared_ptr<T>> m_lObjects; 
  54.   volatile bool m_bDeconstruct = false
  55. }; 

自動(dòng)回收

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

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

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

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

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

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

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

模板支持

使用了模板可以支持通用的對(duì)象:

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

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

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

  1. template<typename... Args> 
  2. std::shared_ptr<T> GetObject(Args&&... args) 
  3.   std::shared_ptr<T> pObject; 
  4.   { 
  5.     std::lock_guard<std::mutex> 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>(args)...), m_fObjDeleter); 
  16.   } 
  17.   return pObject; 

其他

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

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

參考

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

 

 

責(zé)任編輯:武曉燕 來(lái)源: 一個(gè)程序員的修煉之路
相關(guān)推薦

2015-11-30 11:14:59

C++對(duì)象池自動(dòng)回收

2020-04-07 14:20:10

RabbitMMySQL數(shù)據(jù)庫(kù)

2024-11-27 08:15:50

2021-06-06 23:40:53

線程池使用場(chǎng)景

2022-10-28 07:15:26

策略模式使用場(chǎng)景UML

2024-01-30 09:43:43

Java緩存技術(shù)

2023-05-16 07:47:18

RabbitMQ消息隊(duì)列系統(tǒng)

2020-10-29 07:16:26

布隆過(guò)濾器場(chǎng)景

2019-10-25 10:35:49

Java用法場(chǎng)景

2018-08-15 09:48:27

數(shù)據(jù)庫(kù)Redis應(yīng)用場(chǎng)景

2023-06-06 08:18:24

Kafka架構(gòu)應(yīng)用場(chǎng)景

2015-06-26 11:33:23

Python裝飾器使用場(chǎng)景實(shí)踐

2021-08-06 10:43:56

Kubernetes容器

2020-06-16 15:40:32

閉鎖柵欄線程

2019-04-10 15:43:12

SDN場(chǎng)景網(wǎng)絡(luò)架構(gòu)

2013-12-25 16:03:39

GitGit 命令

2021-08-23 12:00:41

云計(jì)算

2025-04-02 00:35:00

CMS垃圾回收器

2013-10-15 10:11:33

產(chǎn)品測(cè)試使用場(chǎng)景產(chǎn)品

2024-12-31 07:56:33

Disruptor內(nèi)存有界隊(duì)列消費(fèi)模式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)