假設我們有一些類似于以下偽代碼的內容,目標是同時實作并發并利用 RAII:
class Foo {
public:
vector<int> nums;
mutex lock;
};
class Bar {
public:
Bar(Foo &foo) : m_foo(foo)
{
lock_guard<mutex>(foo.lock);
m_num = foo.nums.back();
foo.nums.pop_back();
}
~Bar()
{
lock_guard<mutex>(foo.lock);
foo.nums.push_back(m_num);
}
private:
Foo &m_foo;
int m_num;
};
然后,假設我們可能有任意數量的 Bar 實體,其想法是當它們超出范圍時,解構式會將其持有的“資源”回傳給控制器 Foo 類。但是,我們還需要確保執行緒安全,因此需要鎖定。然而,我對這種設計有點警惕,因為在解構式中使用互斥鎖在直覺上似乎是個壞主意。我是不是想太多了,或者如果沒有,有沒有更好的方法來利用 RAII?
uj5u.com熱心網友回復:
將互斥鎖鎖定在解構式中并沒有本質上的錯誤。例如,可能需要使共享資源成為執行緒安全的。因此,釋放共享資源的所有權可能需要鎖定互斥體。如果 RAII 只是在多執行緒編程中分崩離析,它就不是一個非常有用的工具。實際上,對 astd::shared_ptr的控制塊的訪問是執行緒安全的,包括在共享指標銷毀期間遞減參考計數器時。顯然,這通常是用原子操作而不是互斥鎖來實作的(不要參考我的話),但背景關系是相同的:您在銷毀期間釋放共享資源的所有權,并且必須記錄在執行緒安全的方式。
但是,請記住:鎖定互斥鎖可能會引發例外,您應該(幾乎)始終使用 try/catch 來吸收解構式中的例外。否則,如果堆疊由于另一個例外而已經展開,程式將立即且不可恢復地終止,無論呼叫代碼是否配備了吸收原始例外和/或解構式的例外。
但是可能有一種方法可以重組代碼以完全避免該問題: ABar實際上并不需要對 a 的參考Foo;它只需要一個int. 在您的代碼中,Bar請求int來自給定的Foo. 當Bar銷毀時,需要將其int歸還給它,Foo以便它可以回收;這需要Foo在其整個生命周期中存盤對它的內部參考,以便它可以在銷毀期間與它通信。相反,考慮在構造時直接將其int交給,并在破壞時將其拿走。這是依賴注入背后的驅動原理Barint,它構成了“SOILD”中的“D”。因此,它帶來了依賴注入的所有典型優勢(例如,提高Bar類的可測驗性)。
例如,可以在管理Foo物件及其所有關聯Bar物件的更大類中跟蹤此邏輯。這是一些偽代碼,但確切的介面細節將取決于您的應用程式:
class BarPool:
Foo foo;
Map<int, Bar> bars;
mutex m;
BarPool(Foo foo) : foo(foo) {}
int add_bar():
lock m;
// Note: foo.pop() should probably be made thread-safe
// by internally locking / unlocking foo's mutex
int i = foo.pop()
bars.add(i, new Bar(i));
unlock m;
return i
void remove_bar(int i):
lock m;
// foo.push() should also probably be made thread-safe
bars.remove(i)
foo.push(i)
unlock m;
...
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/512398.html
標籤:C 同步莱伊
