C++怎麼實現線程安全
muduo庫學習筆記1-C++多線程系統編程
- 網上都說這本書很適合初學者入門學習, 我今天開始準備從頭再來;
第一章線程安全的對象管理
- 對象的生與死不能由對象自身擁有的mutex(互斥器)來保護;
- 如何避免對象析構時可能存在的race conditon(競態條件)是C++多線程編程面臨的基本問題, C++借用shared_ptr和weak_ptr完美解決;
- shared_ptr和weak_ptr是實現線程安全的Observer設計模式的必備技術;
當析構函數遇到多線程
- C++要求程序員本身管理對象的生命期, 這在多線程環境下顯得尤其困難; 由於析構的時候會出現一些問題:
- 在即將析構一個對象時, 怎麼知道其餘線程正在使用該對象的成員函數;
- 如何保證我在使用一個對象的時候, 沒有其餘線程來析構這個對象;
- 調用一個對象以前, 如何知道這個對象還活着, 它的析構函數會不會碰巧執行到一半?
- 能夠簡單的經過shared_ptr進行一勞永逸的解決這些問題;
- 什麼是線程安全?
- 一個線程安全的類(class)應當知足三個條件:
- 多個線程同時訪問時, 其表現出正確的行爲;
- 不管操做系統如何調度這些線程, 不管這些線程的執行順序如何交織(interleaving);
- 調用端的代碼無需額外的同步或其餘協調動做;
- 鎖的封裝均可以進行臨界區的處理(Critical section) -- 也就是把加鎖放在構造函數, 把解鎖放在析構函數, 這樣就能夠只加鎖, 無論解鎖就行了;
對象的建立很簡單
- 對此昂的構造要作到線程安全, 惟一的要求是在構造起見不要泄露this指針;
- 不要在構造函數中註冊任何回調;
- 也不要在構造函數把this指針傳給跨線程的對象;
- 幾遍在構造函數的最後一行也不行;
- 若是this指針被泄漏給其餘對象, 多是一個半成品;
互斥變量的銷燬太難
- 由於析構函數中會把mutex銷燬;
- 做爲成員的mutex不能保護析構
- 一個函數若是要鎖住相同類型的多個對象, 爲了保證始終按相同的順序加鎖, 咱們能夠比較mutex對象的地址, 始終加鎖地址較小的;
- 對象的三種關係: composition(組合), aggregation(聚合), association(關聯);
- 解決空懸指針的辦法是引入一層間接層, 更好的方法是使用引用計數;
神器shared_ptr/weak_ptr
- shared_ptr是引用計數型智能指針;
- weak_ptr也是一個引用計數型智能指針, 但它不增長對象的引用計數, 即弱引用(weak);
- C++的內存問題大體有這麼幾個方面:
- 緩衝區溢出(buffer overrun);
- 空懸指針/野指針;
- 重複釋放(double delete);
- 內存泄漏(memory leek);
- 不配對的new[]/delete;
- 內存碎片(memory fragmetation);
- scoped_ptr/shared_ptr/weak_ptr都是值語意;
- 多線程訪問同一個shared_ptr, 正確的作法是用mutex保護;
- shared_ptr技術與陷阱;
- 意外延長水箱的生命週期: shared_ptr是強引用, 只要有一個指向x對象的shared_ptr存在, 該對象就不會析構;
- shared_ptr拷貝開銷比原始指針高;
- 析構函數在建立時被捕獲;
- 現成的RAII handle, RAII(資源獲取即初始化)是C++語言區別於其餘全部編程語言的最重要的特性, 一個不懂RAII的C++程序員不是一個合格的C++程序員;
- shared_ptr是管理共享資源的利器, 須要注意避免循環引用, 一般的作法是owner持有指向child的shared_ptr, child持有指向owner的weak_ptr;
對象池
- 若是對象還活着, 就調用它的成員函數, 不然忽略它;
- 用流水線, 生產者消費者, 任務隊列這些有規律的機制, 最低限度地共享數據;
歡迎關注本站公眾號,獲取更多信息