[英]Memory corruption when using boost::shared_ptr in a multithreaded environment
[英]boost::shared_ptr and multithreaded access
我正在嘗試實現一個多線程框架,其中在網絡線程運行的每個幀的末尾創建輸出對象,以便另一個線程可以在其幀的開頭獲取最新的“完成的輸出”指針並且知道它對輸出對象中存儲的任何數據都具有安全且完整的只讀訪問權限。
我的方法(很早)主要涉及以下代碼塊:
NetworkHandler-
void NetworkHandler::Tick()
{
// Tick on our io_service
m_ios.poll();
// Assemble new output data
this->AssembleOutput();
}
ClientNetworkHandler-
void ClientNetworkHandler::AssembleOutput()
{
std::tr1::shared_ptr<crusade::task::TaskOutput> newOutput(new crusade::task::TaskOutput());
newOutput->m_outputElements["connected"] = std::tr1::shared_ptr<crusade::task::TaskOutputElement>(new NetworkConnectedTaskOutputElement(this->m_isConnected));
this->m_latestOutput.swap(newOutput);
}
PyCruHandler-
void PyCruHandler::Tick()
{
printf("PyCruHandler\n");
// Get any necessary inputs from other threads
m_latestNetworkOutput.swap(crusade::task::THManager::GetInstance()->GetTaskHandler(crusade::task::THManager::TH_NETWORK)->GetLatestOutput());
// Other unrelated processing to go here
}
基本上,ClientNetworkHandler和PyCruHandler在單獨的線程上獨立運行。 PyCruHandler從未真正使用m_latestNetworkOutput的副本執行任何操作; 我已經注釋掉了其他任何可以通過任何方式訪問它的實例,但仍然存在以下問題:
如果我允許兩個線程都調用swap(或operator =等效項),那么最終(通常在運行2秒內,但有時要花幾分鍾),我將在操作符new或分配器刪除某些操作時收到以下錯誤分類:
“ HEAP:釋放后在2bab3dc上修改的Free Heap塊2bab3b0 Windows觸發了一個斷點。
這可能是由於堆損壞所致,表明存在錯誤……等。”
我只是一個新手,但對我來說,這似乎表明shared_ptr對象之間存在線程安全和對時間敏感的訪問問題。 但是,我對shared_ptr線程安全的細微差別的解釋(無論在這里還是在其他地方)都感到尷尬地困惑-一段讀數表明引用計數是線程安全的,因此可以安全地復制shared_ptr(但它們的內部對象是不會是線程安全的),其他閱讀資料表明shared_ptr中實際上沒有任何有用的線程安全性。 我已經閱讀了有關shared_ptrs線程安全性的增強文檔,但在我腦海中仍然不清楚這是否應該成為我的代碼中的問題。
我的問題是,這里有人可以檢測到我所做的任何明顯缺陷嗎? 我的目標是我可以訪問仍由擁有線程存儲的最新輸出對象,然后該對象將不會被刪除(即使在擁有線程移至以后的輸出之后),直到輸出的每個用戶都使用完為止也一樣 在我看來,共享指針似乎是完美的……但是,經過3個小時的撞擊,我開始感到奇怪了……
預先非常感謝,如果以某種方式發布錯誤,我深表歉意。 這是我第一次來,就協議而言,FAQ似乎很悠閑!
這里最好的資源可能是文檔 :
線程安全
shared_ptr對象提供與內置類型相同的線程安全級別。 多個線程可以同時“讀取” shared_ptr實例(僅使用const操作訪問)。 多個線程可以同時將不同的shared_ptr實例“寫入”(使用諸如operator =或reset這樣的可變操作來訪問)(即使這些實例是副本,並且在下面共享相同的引用計數)。
例子:
shared_ptr<int> p(new int(42));
//--- Example 1 ---
// thread A
shared_ptr<int> p2(p); // reads p
// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe
//--- Example 2 ---
// thread A
p.reset(new int(1912)); // writes p
// thread B
p2.reset(); // OK, writes p2
//--- Example 3 ---
// thread A
p = p3; // reads p3, writes p
// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
//--- Example 4 ---
// thread A
p3 = p2; // reads p2, writes p3
// thread B
// p2 goes out of scope: undefined, the destructor is considered a "write access"
//--- Example 5 ---
// thread A
p3.reset(new int(1));
// thread B
p3.reset(new int(2)); // undefined, multiple writes
我將假設您代碼中的m_latestOutput
是shared_ptr
在這種情況下,您遇到的示例與數字5(多次寫入)最為相似。
根據文檔,您必須同步進行同時讀寫。 交換都是:)。 考慮使用operator =()代替,那只能是寫操作。
另外,如果客戶端線程要使對象保持活動狀態直到完成,則必須復制m_latestNetworkOutput(將被讀取)(我認為這就是您在PyCruHandler :: Tick()中所做的事情)。
無論如何,您將不得不同步寫入:
this->m_latestOutput.swap(newOutput);
並閱讀:
m_latestNetworkOutput.swap(crusade::task::THManager::GetInstance()->GetTaskHandler(crusade::task::THManager::TH_NETWORK)->GetLatestOutput());
然后將交換更改為分配-交換后不需要舊指針,是嗎?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.