為什么下面的代碼片段中沒有競態條件,
#include<memory>
#include<thread>
std::shared_ptr<int> g_s = std::make_shared<int>(1);
void f1(std::shared_ptr<int> sp)
{
std::shared_ptr<int>l_s1 = sp; // read g_s
}
void f2()
{
std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
std::thread th(f1, g_s);
th.detach();
g_s = l_s2; // write g_s
}
int main()
{
std::thread(f2).join();
}
而下面的代碼片段中存在競爭條件?
#include<memory>
#include<thread>
std::shared_ptr<int> g_s = std::make_shared<int>(1);
void f1(std::shared_ptr<int>& sp)
{
std::shared_ptr<int>l_s1 = sp; // read g_s
}
void f2()
{
std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
std::thread th(f1, std::ref(g_s));
th.detach();
g_s = l_s2; // write g_s
}
int main()
{
std::thread(f2).join();
}
我目前對這個問題的想法見第一個答案。但我還不太確定。有人可以闡明這件事嗎?
uj5u.com熱心網友回復:
首先,std::shared_ptr保證對底層控制塊的訪問是執行緒安全的。
對于后一個代碼片段,原始指標和指向控制塊的指標都可能由不同的執行緒同時讀取和寫入,這將構成競爭。
對于前一種,由于按值void f1(std::shared_ptr<int> sp)傳遞引數(即std::shared_ptr<T> sp),因此讀取原始指標和指向控制塊的指標與sp將所述指標分配給 是獨立的g_s。所以不存在資料競爭條件。
uj5u.com熱心網友回復:
std::shared_ptr本身不是執行緒安全的,這意味著不能從兩個執行緒同時修改 std::shared_ptr<> 的同一物件(實體)。
它不是執行緒安全的,因為 shared_ptr<> 的實作內部沒有互斥鎖,為了速度。
您的第一個代碼片段僅傳遞 shared_ptr<> 實體的副本,從不參考,因此兩個執行緒修改它們的不同副本。這是執行緒安全的。
第二個代碼片段傳遞對第二個執行緒的參考,因此兩個執行緒修改 shared_ptr<> 的同一實體,這不是執行緒安全的。
為什么修改共享指標的副本是執行緒安全的?我們需要研究它是如何實作的。
共享指標由物件本身的指標和指向包含兩個計數器的結構的指標組成,一個計算共享指標的參考次數,第二個計算弱指標參考的次數。計數器通常在堆上分配,因此指向相同堆計數器的指標可以在多個共享指標副本之間共享。
當創建共享指標的副本時,指向其計數器的堆指標被復制到共享指標的其他副本,并且共享指標計數器自動遞增。
當共享指標的副本被銷毀時,它的計數器會自動遞減。如果它是最后一個副本,那么計數器將從堆中釋放。
為什么計數器應該以原子方式遞增/遞減?因為同時其他執行緒中的共享指標的其他副本可能被構造或銷毀,因此其他執行緒將同時增加/減少計數器。
如果計數器同時在不同執行緒上遞增/遞減,那么它應該以原子方式發生,因此該值被讀取-修改-存盤為單個操作。這是為了避免計數器本身內部的競爭條件。
原子操作通常在std::atomic的幫助下完成。
uj5u.com熱心網友回復:
幫助想要更好地了解資料競賽的讀者shared_ptr。
此代碼段沒有任何競爭條件:
#include<memory>
#include<thread>
std::shared_ptr<int> g_s = std::make_shared<int>(1);
std::weak_ptr<int> w_p{g_s};
void f1(std::weak_ptr<int>& wp)
{
std::shared_ptr<int>l_s1 = wp.lock(); // read g_s
}
void f2()
{
std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
std::thread th(f1, std::ref(w_p));
th.detach();
g_s = l_s2; // write g_s
}
int main()
{
std::thread(f2).join();
}
下面的代碼片段也沒有任何競爭條件:
#include<memory>
#include<thread>
std::shared_ptr<int> g_s = std::make_shared<int>(1);
std::weak_ptr<int> w_p{g_s};
void f1(std::weak_ptr<int> wp)
{
std::shared_ptr<int>l_s1 = wp.lock(); // read g_s
}
void f2()
{
std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
std::thread th(f1, w_p);
th.detach();
g_s = l_s2; // write g_s
}
int main()
{
std::thread(f2).join();
}
由于std::weak_ptr::lock() 創建了一個共享托管物件所有權的新 std::shared_ptr。如果沒有托管物件,即*this 為空,則回傳的shared_ptr 也為空。,wp.Lock()回傳一個臨時的shared_ptr,它與命名的不同g_s。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/479433.html
上一篇:遇到例外時重啟python程式
