2013-02-27 84 views
0

最近我開始在一個傳統項目上工作,並試圖修復段錯誤(雙刪除)。它們中的很多正在發生在boost::shared_ptr析構函數或operator=(在包含shared_ptr的對象上)。代碼包含shared_ptr-s的大量使用,包括複製,重置(),分配等。根據boost docs,我們沒有有效的用法 - 它在多個線程中不能安全地銷燬/複製/重置相同的shared_ptr。boost :: shared_ptr drop-in replacement

每次鎖定似乎都不可能,所以即時搜索boost :: shared_ptr的插入替換。所以問題是:如果我用std::shared_ptrstd::tr1::shared_ptr替換所有boost::shared_ptr將解決此問題?看起來tr1是更安全的版本,但對我來說並不明確。第二個問題 - C++ 0x版本比tr1更好嗎? (注意,我們有GCC 4.4.6,並且不能升級)

Accoring到gcc docs,C++ 11的std :: shared_ptr的應該解決這個問題了,但是我不知道GCC4.4版本...

UPD :只要maked實驗,現在我知道所有的3個實現沒有段錯誤的代碼(GCC 4.4)..看來我應該做的自定義類或也許其他的解決辦法...

#include <iostream> 
#include <boost/thread.hpp> 
#include <boost/shared_ptr.hpp> 

typedef boost::shared_ptr<int> ptrtype; 

ptrtype p(new int); 

void test() { 
     for(long i=0; i<1000000; ++i) { 
       ptrtype p1 = p; 
       p = ptrtype(); 
       p.reset(new int); 
     } 
} 

int main() { 
     boost::thread_group tg; 
     for(int i=0; i<100; ++i) tg.add_thread(new boost::thread(test)); 
     tg.join_all(); 
     std::cout << "Normal exit\n"; 
     return 0; 
} 
+3

boost :: shared_ptr非常穩定。我不認爲用另一種實現替換它會「解決」任何事情。如果你看到兩次刪除,那麼你會繼續看到這些,直到你找到並修復你的錯誤。 – Bukes 2013-02-27 19:28:03

+4

「每次鎖定似乎都不可能」表示您有線程問題。改變智能指針的實現並不會爲你購買任何東西。 boost :: shared_ptr不是越野車。 – 2013-02-27 19:28:59

+0

你在尋找一個線程安全的共享指針嗎? – 2013-02-27 19:31:35

回答

1

第1步:像這樣建立一個類,並用它替換boost::shared_ptr<T>的用法。

template<typename T> 
struct trivial_ptr { 
    T* t; 
    template<typename U> 
    void reset(U* p) {t=p;} 
    void reset(T* p = NULL) { t=p; } 
    template<typename U> 
    trivial_ptr<T>& operator=(trivial_shared_ptr<U>const& o) { 
    t = o.t; 
    return *t; 
    } 
    explicit trivial_ptr(T* p):t(p) {} 
    ... 
}; 

這個類不是爲了運行,而只是用正確的接口進行編譯。編譯完成後,您可以確保知道正在使用的boost::shared_ptr接口的哪些部分。 (您是否使用自定義刪除程序?等 - 問題可能會更困難或更容易,以上可以幫助測試它)

一旦你在那裏,你可以計算出寫一個shared_ptr<T>它處理多個線程同時訪問同一個變量。

現在,這是非常混亂。如果一個線程reset是給定的shared_ptr,而另一個線程從中讀取,則在讀取線程訪問該指針時,從讀取的指針可能完全無效。實際上,您需要警惕所有訪問互斥體中的底層指針,這是非常不切實際的。另一方面,如果你擁有的是多個讀者,而不是一個讀者和一個作者,那麼你的狀態就會變好 - 理論上,你可以通過在引用計數上使用適當的鎖來解決問題碼。

但是,你實際描述的似乎涉及多個線程讀取和寫入同一個變量。從根本上講,shared_ptr變量的線程安全性不會解決。

+0

感謝您的回覆。也許我應該切換到自定義類..我的實驗顯示所有3個標準實現失敗此用例( – PSIAlt 2013-02-27 21:06:47

+0

@ PSIAlt:你沒有讀過他的最後一段嗎?你不能通過創建一個新的指針類型來解決問題。除非你將要互斥 - 包裝每一個指針實例,即使如此,你仍然會遇到問題,因爲有人會認爲舊指針是在附近。你無法逃避你已經交給了一個可怕的錯誤代碼庫的事實,而你需要解決它。 – 2013-02-27 21:33:23

+0

@NicolBolas唉,看來真的有比沒有別的辦法..不知道又是什麼。它看起來像我沒有機會沒有經紀東西改寫這一點。但是,感謝指點一下鎖定 – PSIAlt 2013-02-27 21:42:18

0

std::shared_ptr可能使用原子整數,如果編譯器/體系結構/實現支持/使用它。但我不會賭它,這樣做會使你的代碼更加便攜。但它可能只是一個臨時的解決方法(例如,讓一個正在運行的程序,以便您瞭解代碼應該執行的操作)。

編寫你自己的shared_ptr包裝可能是一個選項,但你的代碼仍然需要審覈死鎖。

+3

'std :: shared_ptr'必須是線程安全的......但*僅*作爲線程安全的'boost: :shared_ptr'。也就是說,兩個不同的shared_ptr對象可以引用相同的數據,並且刪除會很好。但是你不能在兩個不同的線程中查找* same * shared_ptr對象。後者是OP的問題。 – 2013-02-27 21:31:13

+0

'shared_ptr' **是必需**爲線程安全的,但僅用於同時複製實例,不能同時修改它們 – 2013-02-28 18:13:23

1

您似乎遇到的問題是試圖在兩個單獨的線程(也就是數據競賽)中修改同一個變量實例。 shared_ptr不會比int更受保護。您對gcc文檔的引用說明了同樣的事情(「與內置類型相同的線程安全級別」)。嘗試修改兩個不同線程中的shared_ptr的同一實例需要某種同步來防止數據競爭。試圖修改指向同一對象的shared_ptr的兩個不同實例是OK(沒有數據競爭或shared_ptr必須實現防止內部數據競爭所需的任何內容)。試圖修改它們指向的對象也是數據競賽。

相關問題