C++中各种智能指针的实现及弊端(四)
生活随笔
收集整理的這篇文章主要介紹了
C++中各种智能指针的实现及弊端(四)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
C++中各種智能指針的實現及弊端(四)
文章目錄
- C++中各種智能指針的實現及弊端(四)
- 一、std::shared_ptr
- 二、std::shared_ptr的線程安全問題:
一、std::shared_ptr
std::shared_ptr文檔
int main() {// shared_ptr通過引用計數支持智能指針對象的拷貝shared_ptr<Date> sp(new Date);shared_ptr<Date> copy(sp);cout << "ref count:" << sp.use_count() << endl;cout << "ref count:" << copy.use_count() << endl;return 0; }- shared_ptr的原理:是通過引用計數的方式來實現多個shared_ptr對象之間共享資源。例如:
針了
詳細請參考:引用計數+淺拷貝=解決淺拷貝
// 模擬實現一份簡答的SharedPtr,了解原理 #include <thread> #include <mutex> template <class T> class SharedPtr { public:SharedPtr(T* ptr = nullptr): _ptr(ptr), _pRefCount(new int(1)), _pMutex(new mutex){}~SharedPtr() {Release();}SharedPtr(const SharedPtr<T>& sp): _ptr(sp._ptr), _pRefCount(sp._pRefCount), _pMutex(sp._pMutex){AddRefCount();}// sp1 = sp2SharedPtr<T>& operator=(const SharedPtr<T>& sp){//if (this != &sp)if (_ptr != sp._ptr){// 釋放管理的舊資源Release();// 共享管理新對象的資源,并增加引用計數_ptr = sp._ptr;_pRefCount = sp._pRefCount;_pMutex = sp._pMutex;AddRefCount();}return *this;}T& operator*() {return *_ptr;}T* operator->() {return _ptr;}int UseCount() {return *_pRefCount;}T* Get() { return _ptr; }void AddRefCount(){// 加鎖或者使用加1的原子操作_pMutex->lock();++(*_pRefCount);_pMutex->unlock();} private:void Release(){bool deleteflag = false;// 引用計數減1,如果減到0,則釋放資源_pMutex.lock();if (--(*_pRefCount) == 0){delete _ptr;delete _pRefCount;deleteflag = true;}_pMutex.unlock();if(deleteflag == true)delete _pMutex;}private:int* _pRefCount; // 引用計數T* _ptr; // 指向管理資源的指針mutex* _pMutex; // 互斥鎖 };int main() {SharedPtr<int> sp1(new int(10));SharedPtr<int> sp2(sp1);*sp2 = 20;cout << sp1.UseCount() << endl;cout << sp2.UseCount() << endl;SharedPtr<int> sp3(new int(10));sp2 = sp3;cout << sp1.UseCount() << endl;cout << sp2.UseCount() << endl;cout << sp3.UseCount() << endl;sp1 = sp3;cout << sp1.UseCount() << endl;cout << sp2.UseCount() << endl;cout << sp3.UseCount() << endl; return 0; }上面的代碼采用加鎖的方式;主要是為了:防止計數_pRefCount出現錯亂問題
二、std::shared_ptr的線程安全問題:
通過下面的程序我們來測試shared_ptr的線程安全問題。需要注意的是shared_ptr的線程安全分為兩方面:
1. 智能指針對象中引用計數是多個智能指針對象共享的,兩個線程中智能指針的引用計數同時++或–,這個操作不是原子的,引用計數原來是1,++了兩次,可能還是2.這樣引用計數就錯亂了。會導致資源未釋放或者程序崩潰的問題。所以只能指針中引用計數++、–是需要加鎖的,也就是說引用計數的操作是線程安全的。
2. 智能指針管理的對象存放在堆上,兩個線程中同時去訪問,會導致線程安全問題。
// 1.演示引用計數線程安全問題,就把AddRefCount和SubRefCount中的鎖去掉 // 2.演示可能不出現線程安全問題,因為線程安全問題是偶現性問題,main函數的n改大 //一些概率就變大了,就容易出現了。 // 3.下面代碼我們使用SharedPtr演示,是為了方便演示引用計數的線程安全問題, //將代碼中的SharedPtr換成shared_ptr進行測試,可以驗證庫的shared_ptr, //發現結論是一樣的。void SharePtrFunc(SharedPtr<Date>& sp, size_t n) {cout << sp.Get() << endl;for (size_t i = 0; i < n; ++i){// 這里智能指針拷貝會++計數,智能指針析構會--計數,這里是線程安全的。SharedPtr<Date> copy(sp);// 這里智能指針訪問管理的資源,不是線程安全的。所以我們看看這些值兩個線程++了2n次,但是最//終看到的結果,并一定是加了2ncopy->_year++;copy->_month++;copy->_day++;} }int main() {SharedPtr<Date> p(new Date);cout << p.Get() << endl;const size_t n = 100;thread t1(SharePtrFunc, p, n);thread t2(SharePtrFunc, p, n);t1.join();t2.join();cout << p->_year << endl;cout << p->_month << endl;cout << p->_day << endl;return 0; }通過上面的代碼驗證了如果把AddRefCount和SubRefCount中的鎖去掉,可能會造成線程安全問題;
因為線程是搶占式執行的,那個線程先訪問計數_pRefCount是不確定的,所以就可能會造成計數_pRefCount錯亂,不是線程安全的。
但是上面的代碼還是有問題的:存在循環引用問題;
解決方法:解決循環引用問題
總結
以上是生活随笔為你收集整理的C++中各种智能指针的实现及弊端(四)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++中各种智能指针的实现及弊端(五)
- 下一篇: C++版二叉树非递归遍历