智慧指標 shared ptr 的使用方法
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
基於Boost庫, C++11 加入了shared_ptr和weak_ptr. 它們最早在TR1中就被引入, 但在C++11中, 在Boost的基礎上又加入了新的功能.
std::shared_ptr
std::shared_ptr<int> p1(new int(5));std::shared_ptr<int> p2 = p1; // 都指向同一記憶體. p1.reset(); // 因為p2還在,所以記憶體沒有釋放.p2.reset(); // 釋放記憶體, 因為沒有shared_ptr指向那塊記憶體了.std::shared_ptr 使用引用計數, 所以有迴圈計數的問題. 為了打破迴圈,可以使用std ::weak_ptr. 顧名思義, weak_ptr是一個弱引用, 只引用, 不計數. 如果一塊記憶體被shared_ptr和weak_ptr同時引用, 當所有shared_ptr析構了之後,不管還有沒有weak_ptr引用該記憶體, 記憶體也會被釋放. 所以weak_ptr不保證它指向的記憶體一定是有效的, 在使用之前需要檢查.std::shared_ptr<int> p1(new int(5));std::weak_ptr<int> wp1 = p1; // 還是隻有p1有所有權. { std::shared_ptr<int> p2 = wp1.lock(); // p1和p2都有所有權 if(p2) // 使用前需要檢查 { // 使用p2 }} // p2析構了, 現在只有p1有所有權. p1.reset(); // 記憶體被釋放. std::shared_ptr<int> p3 = wp1.lock(); // 因為記憶體已經被釋放了, 所以得到的是空指標.if(p3){ // 不會執行到這.}
shared_ptr的成員方法,user_count()的作用是獲得當前物件被引用的次數,reset()的作用是釋放指標對物件的引用,將指標設為空。
shared_ptr的執行緒安全性:
它是這樣說的:
“Boost 文件對於 shared_ptr 的執行緒安全有一段專門的記述,內容如下:
shared_ptr objects offer the same level of thread safety as built-in types. A shared_ptr instance can be "read" (accessed using only const operations) simultaneously by multiple threads. Different shared_ptr instances can be "written to" (accessed using mutable operations such as operator= or reset) simultaneosly by multiple threads (even when these instances are copies, and share the same reference count underneath.)
Any other simultaneous accesses result in undefined behavior.
翻譯為中文如下:
shared_ptr 物件提供與內建型別一樣的執行緒安全級別。一個 shared_ptr 例項可以同時被多個執行緒“讀”(僅使用不變操作進行訪問)。 不同的 shared_ptr 例項可以同時被多個執行緒“寫入”(使用類似 operator= 或 reset 這樣的可變操作進行訪問)(即使這些實 例是拷貝,而且共享下層的引用計數)。
任何其它的同時訪問的結果會導致未定義行為。”
這幾句話比較繁瑣,我總結一下它的意思:
1 同一個shared_ptr被多個執行緒“讀”是安全的。
2 同一個shared_ptr被多個執行緒“寫”是不安全的。
3 共享引用計數的不同的shared_ptr被多個執行緒”寫“ 是安全的。
如何印證上面的觀點呢?
其實第一點我覺得比較多餘。因為在多個執行緒中讀同一個物件,在正常情況下不會有什麼問題。
所以問題就是:如何寫程式證明同一個shared_ptr被多個執行緒"寫"是不安全的?
我的思路是,在多個執行緒中同時對一個shared_ptr迴圈執行兩遍swap。 shared_ptr的swap函式的作用就是和另外一個shared_ptr交換引用物件和引用計數,是寫操作。執行兩遍swap之後, shared_ptr引用的物件的值應該不變。
程式如下:
#include <stdio.h>#include <tr1/memory>#include <pthread.h>using std::tr1::shared_ptr;shared_ptr<int> gp(new int(2000));shared_ptr<int> CostaSwapSharedPtr1(shared_ptr<int> & p){ shared_ptr<int> p1(p); shared_ptr<int> p2(new int(1000)); p1.swap(p2); p2.swap(p1); return p1;}shared_ptr<int> CostaSwapSharedPtr2(shared_ptr<int> & p){ shared_ptr<int> p2(new int(1000)); p.swap(p2); p2.swap(p); return p;}void* thread_start(void * arg){ int i =0; for(;i<100000;i++) { shared_ptr<int> p= CostaSwapSharedPtr2(gp); if(*p!=2000) { printf("Thread error. *gp=%d \n", *gp); break; } } printf("Thread quit \n"); return 0;}int main(){ pthread_t thread; int thread_num = 10, i=0; pthread_t* threads = new pthread_t[thread_num]; for(;i<thread_num;i++) pthread_create(&threads[i], 0 , thread_start , &i); for(i=0;i<thread_num;i++) pthread_join(threads[i],0); delete[] threads; return 0;}
這個程式中我啟了10個執行緒。每個執行緒呼叫10萬次 CostaSwapSharedPtr2函式。 在CostaSwapSharePtr2函式中,對同一個share_ptr全域性變數gp進行兩次swap(寫操作), 在函式返回之後檢查gp的值是否被修改。如果gp值被修改,則證明多執行緒對同一個share_ptr執行寫操作是不安全的。
程式執行的結果如下:
Thread error. *gp=1000 Thread error. *gp=1000 Thread quit Thread quit Thread error. *gp=1000 Thread quit Thread error. *gp=1000 Thread quit Thread error. *gp=1000 Thread quit Thread error. *gp=1000 Thread quit Thread error. *gp=1000 Thread quit Thread error. *gp=1000 Thread quit Thread error. *gp=1000 Thread quit Thread quit
10個執行緒有9個出錯。證明多執行緒對同一個share_ptr執行寫操作是不安全的。
我們在程式中,如果不執行CostaSwapSharedPtr2, 改成執行CostaSwapSharedPtr1呢?
CostaSwapSharedPtr1和CostaSwapSharedPtr2的區別在於, 它不是直接對全域性變數gp進行寫操作,而是將gp拷貝出來一份再進行寫操作。執行的結果如下:
[email protected]:~/test/cpp/shared_ptr$ ./bThread quit Thread quit Thread quit Thread quit Thread quit Thread quit Thread quit Thread quit Thread quit Thread quit