[英]Returning shared_ptr and exception safety
我正在閱讀“C ++ Concurrency in action”一書,並嘗試理解線程安全數據結構(例如堆棧)中的異常安全性。 作者說,為了避免競爭條件, pop
應該同時執行這兩個操作 - 彈出和返回堆棧中的項目,但是:
如果pop()函數被定義為返回彈出的值,並將其從堆棧中刪除,則可能存在一個問題:彈出的值只有在修改了堆棧后才返回給調用者,但是復制數據以返回調用者可能會引發異常。
這是建議的解決方案:
std::shared_ptr<T> pop()
{
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw runtime_error("empty");
std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
data.pop();
return res;
}
在這種情況下,我們在make_shared
行上調用了復制構造函數 。 如果復制構造函數拋出異常,則堆棧尚未修改,我們很好。
但是,我不知道它與這個片段的區別很大:
T pop2() {
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw runtime_error("empty");
auto res = data.top();
data.pop();
return res;
}
這里我們在data.top()
行復制構造函數調用,並在返回時移動構造函數 。 同樣,如果復制構造函數拋出異常,我們就很好,因為堆棧尚未修改。 移動contstructor不應該拋出異常。
我錯過了什么嗎? 將shared_ptr
比較返回到返回(可移動) T
什么好處?
如果T
不可移動,您的代碼可能最終會執行多個復制構造。 在這種情況下,使用shared_ptr
在堆上分配副本可能更有效。
即使T
是可移動的,您的代碼仍可能執行多個( 可能代價高昂的)移動構造。 與移動或復制構造任何可能的類型T
相比,已知移動構造和/或復制構造shared_ptr
相對便宜。
當然,您的里程可能會有所不同,具體取決於具體類型,編譯器以及操作環境和硬件。
move-constructor可能會拋出(一般情況下)。 std::shared_ptr<T>
有一個noexcept
移動構造函數。
所以在第一種情況下return res;
不能扔,但在第二種情況下return res;
可能會拋出(並且已經調用了data.pop()
)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.